[e-cvs] cvs commit: e/src/jsrc/org/quasiliteral/term QuasiBuilder.java QuasiBuilderAdaptor.java Term.java Term.updoc TermParser.java term.y

markm@eros.cs.jhu.edu markm@eros.cs.jhu.edu
Fri, 21 Dec 2001 00:30:35 -0500


markm       01/12/21 00:30:35

  Modified:    src      Makefile
               src/jsrc/org/erights/e/develop/assertion T.java
               src/jsrc/org/quasiliteral/quasiterm QAstroArg.java
                        QBuilder.java QTerm.java
               src/jsrc/org/quasiliteral/term QuasiBuilder.java
                        QuasiBuilderAdaptor.java Term.java Term.updoc
                        TermParser.java term.y
  Added:       src/jsrc/org/quasiliteral/quasiterm QAstro.java QAtHole.java
                        QDollarHole.java QFunctor.java QHole.java
                        QSome.java
  Log:
  quasiliteral terms work with quantifiers.  Ready for the 0.8.10epsilon1 release

Revision  Changes    Path
1.131     +2 -2      e/src/Makefile

Index: Makefile
===================================================================
RCS file: /cvs/e/src/Makefile,v
retrieving revision 1.130
retrieving revision 1.131
diff -u -r1.130 -r1.131
--- Makefile	2001/12/18 05:24:40	1.130
+++ Makefile	2001/12/21 05:30:35	1.131
@@ -7,8 +7,8 @@
 
 # Prefix tagging this release's attributes
 PREFIX=E
-DOTVER=0.8.10delta5
-TAGVER=0_8_10delta5
+DOTVER=0.8.10epsilon1
+TAGVER=0_8_10epsilon1
 RELEASE=working
 
 TOP=..



1.2       +90 -88    e/src/jsrc/org/erights/e/develop/assertion/T.java

Index: T.java
===================================================================
RCS file: /cvs/e/src/jsrc/org/erights/e/develop/assertion/T.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- T.java	2001/12/18 05:24:41	1.1
+++ T.java	2001/12/21 05:30:35	1.2
@@ -49,16 +49,18 @@
  */
 public class T {
 
-    static private final String prefix = "Internal: ";
+    static private final String internal = "Internal: ";
 
+    static private final String failed = "Failed: ";
+
     /**
      * If this method is executed, it throws an AssertionError with the
-     * message "Internal:  'Unreachable' code was reached."  Plant
+     * message "Internal: 'Unreachable' code was reached."  Plant
      * such assertions in places the program should never reach (such as
      * the default case in a switch).
      */
     static public void fail() throws AssertionError {
-        throw new AssertionError(prefix + "'Unreachable' code was reached.");
+        throw new AssertionError(internal + "'Unreachable' code was reached.");
     }
 
     /**
@@ -66,7 +68,7 @@
      * given explanation, prefixed by "Internal: ".
      */
     static public void fail(String explanation) throws AssertionError {
-        throw new AssertionError(prefix + explanation);
+        throw new AssertionError(internal + explanation);
     }
 
     /**
@@ -75,7 +77,7 @@
      */
     static public void test(boolean mustBeTrue) throws AssertionError {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix + "Assertion failed.");
+            throw new AssertionError(internal + "Assertion failed.");
         }
     }
 
@@ -87,7 +89,7 @@
                             String explanation)
       throws AssertionError {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
+            throw new AssertionError(internal +
                                      explanation);
         }
     }
@@ -106,7 +108,7 @@
                               int explanation1)
       throws AssertionError {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
+            throw new AssertionError(internal +
                                      explanation0 +
                                      explanation1);
         }
@@ -126,7 +128,7 @@
                             Object explanation1)
       throws AssertionError {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
+            throw new AssertionError(internal +
                                      explanation0 +
                                      explanation1);
         }
@@ -146,7 +148,7 @@
                             Object explanation2)
       throws AssertionError {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
+            throw new AssertionError(internal +
                                      explanation0 +
                                      explanation1 +
                                      explanation2);
@@ -168,7 +170,7 @@
                             Object explanation3)
       throws AssertionError {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
+            throw new AssertionError(internal +
                                      explanation0 +
                                      explanation1 +
                                      explanation2 +
@@ -192,7 +194,7 @@
                             Object explanation4)
       throws AssertionError {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
+            throw new AssertionError(internal +
                                      explanation0 +
                                      explanation1 +
                                      explanation2 +
@@ -218,7 +220,7 @@
                             Object explanation5)
       throws AssertionError {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
+            throw new AssertionError(internal +
                                      explanation0 +
                                      explanation1 +
                                      explanation2 +
@@ -246,7 +248,7 @@
                             Object explanation6)
       throws AssertionError {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
+            throw new AssertionError(internal +
                                      explanation0 +
                                      explanation1 +
                                      explanation2 +
@@ -276,7 +278,7 @@
                             Object explanation7)
       throws AssertionError {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
+            throw new AssertionError(internal +
                                      explanation0 +
                                      explanation1 +
                                      explanation2 +
@@ -289,22 +291,22 @@
     }
 
     /**
-     * If the argument is false, throws an AssertionError with the
-     * given explanation, prefixed by "Internal: ".
+     * If the argument is false, throws a RuntimeException with the
+     * given explanation, prefixed by "Failed: ".
      */
     static public void require(boolean mustBeTrue,
                                String explanation)
-      throws AssertionError {
+      throws RuntimeException {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
-                                     explanation);
+            throw new RuntimeException(failed +
+                                       explanation);
         }
     }
 
     /**
-     * If the argument is false, throws an AssertionError with the
+     * If the argument is false, throws a RuntimeException with the
      * given explanation arguments, concatenated as strings and prefixed
-     * by "Internal: ".
+     * by "Failed: ".
      * <p>
      * Use this routine when you need to avoid paying the overhead of
      * string concatenation ("+") on every test.  It does the concatenation
@@ -313,18 +315,18 @@
     static public void requireSI(boolean mustBeTrue,
                                  Object explanation0,
                                  int explanation1)
-      throws AssertionError {
+      throws RuntimeException {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
-                                     explanation0 +
-                                     explanation1);
+            throw new RuntimeException(failed +
+                                       explanation0 +
+                                       explanation1);
         }
     }
 
     /**
-     * If the argument is false, throws an AssertionError with the
+     * If the argument is false, throws a RuntimeException with the
      * given explanation arguments, concatenated as strings and prefixed
-     * by "Internal: ".
+     * by "Failed: ".
      * <p>
      * Use this routine when you need to avoid paying the overhead of
      * string concatenation ("+") on every test.  It does the concatenation
@@ -333,18 +335,18 @@
     static public void require(boolean mustBeTrue,
                                Object explanation0,
                                Object explanation1)
-      throws AssertionError {
+      throws RuntimeException {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
-                                     explanation0 +
-                                     explanation1);
+            throw new RuntimeException(failed +
+                                       explanation0 +
+                                       explanation1);
         }
     }
 
     /**
-     * If the argument is false, throws an AssertionError with the
+     * If the argument is false, throws a RuntimeException with the
      * given explanation arguments, concatenated as strings and prefixed
-     * by "Internal: ".
+     * by "Failed: ".
      * <p> Use this routine when you need to avoid paying the overhead of
      * string concatenation ("+") on every test.  It does the concatenation
      * only if the test fails.
@@ -353,19 +355,19 @@
                                Object explanation0,
                                Object explanation1,
                                Object explanation2)
-      throws AssertionError {
+      throws RuntimeException {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
-                                     explanation0 +
-                                     explanation1 +
-                                     explanation2);
+            throw new RuntimeException(failed +
+                                       explanation0 +
+                                       explanation1 +
+                                       explanation2);
         }
     }
 
     /**
-     * If the argument is false, throws an AssertionError with the
+     * If the argument is false, throws a RuntimeException with the
      * given explanation arguments, concatenated as strings and prefixed
-     * by "Internal: ".
+     * by "Failed: ".
      * <p> Use this routine when you need to avoid paying the overhead of
      * string concatenation ("+") on every test.  It does the concatenation
      * only if the test fails.
@@ -375,20 +377,20 @@
                                Object explanation1,
                                Object explanation2,
                                Object explanation3)
-      throws AssertionError {
+      throws RuntimeException {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
-                                     explanation0 +
-                                     explanation1 +
-                                     explanation2 +
-                                     explanation3);
+            throw new RuntimeException(failed +
+                                       explanation0 +
+                                       explanation1 +
+                                       explanation2 +
+                                       explanation3);
         }
     }
 
     /**
-     * If the argument is false, throws an AssertionError with the
+     * If the argument is false, throws a RuntimeException with the
      * given explanation arguments, concatenated as strings and prefixed
-     * by "Internal: ".
+     * by "Failed: ".
      * <p> Use this routine when you need to avoid paying the overhead of
      * string concatenation ("+") on every test.  It does the concatenation
      * only if the test fails.
@@ -399,21 +401,21 @@
                                Object explanation2,
                                Object explanation3,
                                Object explanation4)
-      throws AssertionError {
+      throws RuntimeException {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
-                                     explanation0 +
-                                     explanation1 +
-                                     explanation2 +
-                                     explanation3 +
-                                     explanation4);
+            throw new RuntimeException(failed +
+                                       explanation0 +
+                                       explanation1 +
+                                       explanation2 +
+                                       explanation3 +
+                                       explanation4);
         }
     }
 
     /**
-     * If the argument is false, throws an AssertionError with the
+     * If the argument is false, throws a RuntimeException with the
      * given explanation arguments, concatenated as strings and prefixed
-     * by "Internal: ".
+     * by "Failed: ".
      * <p> Use this routine when you need to avoid paying the overhead of
      * string concatenation ("+") on every test.  It does the concatenation
      * only if the test fails.
@@ -425,22 +427,22 @@
                                Object explanation3,
                                Object explanation4,
                                Object explanation5)
-      throws AssertionError {
+      throws RuntimeException {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
-                                     explanation0 +
-                                     explanation1 +
-                                     explanation2 +
-                                     explanation3 +
-                                     explanation4 +
-                                     explanation5);
+            throw new RuntimeException(failed +
+                                       explanation0 +
+                                       explanation1 +
+                                       explanation2 +
+                                       explanation3 +
+                                       explanation4 +
+                                       explanation5);
         }
     }
 
     /**
-     * If the argument is false, throws an AssertionError with the
+     * If the argument is false, throws a RuntimeException with the
      * given explanation arguments, concatenated as strings and prefixed
-     * by "Internal: ".
+     * by "Failed: ".
      * <p> Use this routine when you need to avoid paying the overhead of
      * string concatenation ("+") on every test.  It does the concatenation
      * only if the test fails.
@@ -453,23 +455,23 @@
                                Object explanation4,
                                Object explanation5,
                                Object explanation6)
-      throws AssertionError {
+      throws RuntimeException {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
-                                     explanation0 +
-                                     explanation1 +
-                                     explanation2 +
-                                     explanation3 +
-                                     explanation4 +
-                                     explanation5 +
-                                     explanation6);
+            throw new RuntimeException(failed +
+                                       explanation0 +
+                                       explanation1 +
+                                       explanation2 +
+                                       explanation3 +
+                                       explanation4 +
+                                       explanation5 +
+                                       explanation6);
         }
     }
 
     /**
-     * If the argument is false, throws an AssertionError with the
+     * If the argument is false, throws a RuntimeException with the
      * given explanation arguments, concatenated as strings and prefixed
-     * by "Internal: ".
+     * by "Failed: ".
      * <p> Use this routine when you need to avoid paying the overhead of
      * string concatenation ("+") on every test.  It does the concatenation
      * only if the test fails.
@@ -483,17 +485,17 @@
                                Object explanation5,
                                Object explanation6,
                                Object explanation7)
-      throws AssertionError {
+      throws RuntimeException {
         if (mustBeTrue == false) {
-            throw new AssertionError(prefix +
-                                     explanation0 +
-                                     explanation1 +
-                                     explanation2 +
-                                     explanation3 +
-                                     explanation4 +
-                                     explanation5 +
-                                     explanation6 +
-                                     explanation7);
+            throw new RuntimeException(failed +
+                                       explanation0 +
+                                       explanation1 +
+                                       explanation2 +
+                                       explanation3 +
+                                       explanation4 +
+                                       explanation5 +
+                                       explanation6 +
+                                       explanation7);
         }
     }
 }



1.3       +62 -8     e/src/jsrc/org/quasiliteral/quasiterm/QAstroArg.java

Index: QAstroArg.java
===================================================================
RCS file: /cvs/e/src/jsrc/org/quasiliteral/quasiterm/QAstroArg.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- QAstroArg.java	2001/12/19 10:18:08	1.2
+++ QAstroArg.java	2001/12/21 05:30:35	1.3
@@ -9,6 +9,11 @@
 import org.erights.e.elib.tables.FlexList;
 import org.erights.e.elib.tables.Selfless;
 import org.erights.e.elib.tables.Twine;
+import org.erights.e.elib.tables.EList;
+import org.erights.e.elib.base.Ejector;
+import org.erights.e.elib.base.ClassDesc;
+import org.erights.e.elib.slot.ValueGuard;
+import org.erights.e.elib.prim.E;
 import org.quasiliteral.astro.AstroArg;
 import org.quasiliteral.astro.AstroBuilder;
 import org.quasiliteral.base.MatchMaker;
@@ -29,6 +34,16 @@
   AstroArg, ValueMaker, MatchMaker {
 
     /**
+     *
+     */
+    static /*package*/ final int[] EMPTY_INDEX = {};
+
+    /**
+     *
+     */
+    static /*package*/ ValueGuard EListGuard = ClassDesc.make(EList.class);
+
+    /**
      * @serial Builds Terms using the Schema derived from myTag.
      */
     /*package*/
@@ -56,7 +71,7 @@
      * @return :Astro
      */
     public Object substitute(Object[] args) {
-        ConstList list = substSlice(args, "");
+        ConstList list = substSlice(args, EMPTY_INDEX);
         T.require(list.size() == 1,
                   "Must be singleton: ", list);
         return list.get(0);
@@ -69,18 +84,36 @@
      *             lists of terms, for which the outer list may only be zero
      *             or one long, the middle lists may be any length, and the
      *             inner lists must all have at least one element.
-     * @param rank :String(('?'|'+'|'*')*)
+     * @param index Further indexes after a hole's hole-num.  For example,
+     *              If a dollar-hole's hole-num is 3 and index is [4,5], then
+     *              the dollar-hole would evaluate to args[3][4][5].
      * @return :(ConstList of(Astro))
      */
-    public abstract ConstList substSlice(Object[] args, String rank);
+    public abstract ConstList substSlice(Object[] args, int[] index);
 
     /**
+     * Transitive snapshot through all substructure that's instanceof EList.
+     */
+    static private Object transSnapshot(Object thing) {
+        if (! (thing instanceof EList)) {
+            return thing;
+        }
+        EList list = (EList)thing;
+        int len = list.size();
+        Object[] result = new Object[len];
+        for (int i = 0; i < len; i++) {
+            result[i] = transSnapshot(list.get(i));
+        }
+        return ConstList.fromArray(result);
+    }
+
+    /**
      *
      */
     public ConstList matchBind(Object[] args, Object specimen) {
         FlexList bindings = FlexList.make();
         if (matchBind(args, specimen, bindings)) {
-            return bindings.snapshot();
+            return (ConstList)transSnapshot(bindings);
         } else {
             return null;
         }
@@ -100,7 +133,7 @@
         return 1 == matchBindSlice(args,
                                    ConstList.EmptyList.with(specimen),
                                    bindings,
-                                   "");
+                                   EMPTY_INDEX);
     }
 
     /**
@@ -109,7 +142,11 @@
      *             String)}
      * @param specimenList :(ConstList of(Astro))
      * @param bindings Like 'args', but by extraction from specimen
-     * @param rank :String(('?'|'+'|'*')*)
+     * @param index Further indexes after a hole's hole-num.  For example,
+     *              If a dollar-hole's hole-num is 3 and index is [4,5], then
+     *              the dollar-hole would access args[3][4][5].  Similarly,
+     *              an at-hole with hole-num 3 would store into
+     *              bindings[3][4][5].
      * @return How many elements of specimen are matched?  Zero indicates a
      *         successful match of no elements, so -1 is used to instead
      *         indicate a failed match.
@@ -117,7 +154,24 @@
     public abstract int matchBindSlice(Object[] args,
                                        ConstList specimenList,
                                        FlexList bindings,
-                                       String rank);
+                                       int[] index);
+
+    /**
+     * For this substree and this index-prefix, how many index elements should
+     * be enumerated?
+     * <p>
+     * If this subtree has no dollar-holes, it should just return shapeSoFar.
+     * The initial shapeSoFar is -1 (meaning "indeterminate"), so a tree with
+     * no dollar-holes will just return -1.  An non-ranking inner node (eg, a
+     * QTerm) just asks all its children, passing to each the shapeSoFar from
+     * the previous.
+     * <p>
+     * All the rest of the semantics is specific to dollar-hole or to raking
+     * nodes, so see the documentation there.
+     */
+    /*package*/ abstract int getShape(Object args[],
+                                      int[] prefix,
+                                      int shapeSoFar);
 
     /**
      * What source text was originally lexed or parsed to produce this node,
@@ -152,7 +206,7 @@
     /**
      *
      */
-    public String asText() {
+    public String toString() {
         StringWriter strWriter = new StringWriter();
         try {
             prettyPrintOn(new TextWriter(strWriter));



1.4       +50 -9     e/src/jsrc/org/quasiliteral/quasiterm/QBuilder.java

Index: QBuilder.java
===================================================================
RCS file: /cvs/e/src/jsrc/org/quasiliteral/quasiterm/QBuilder.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- QBuilder.java	2001/12/19 22:36:13	1.3
+++ QBuilder.java	2001/12/21 05:30:35	1.4
@@ -3,6 +3,8 @@
 import org.erights.e.elib.tables.EList;
 import org.erights.e.elib.tables.FlexList;
 import org.erights.e.elib.tables.Twine;
+import org.erights.e.elib.tables.EmptyTwine;
+import org.erights.e.develop.assertion.T;
 import org.quasiliteral.astro.Astro;
 import org.quasiliteral.astro.AstroArg;
 import org.quasiliteral.astro.AstroBuilder;
@@ -16,6 +18,8 @@
 import org.quasiliteral.term.TermBuilder;
 import org.quasiliteral.term.TermParser;
 
+import java.math.BigInteger;
+
 //This file is hereby placed in the public domain
 
 /**
@@ -94,14 +98,28 @@
     }
 
     /**
-     *
+     * Returns the QTerm '<functor>(<args>...)'.
+     * <p>
+     * Note that the QTerm constructor will first convert 'functor' using
+     * {@link QAstro#asFunctor()}, so if 'functor' is originally a term-hole,
+     * the functor of the resulting QTerm will be a corresponding
+     * functor-hole.
      */
     public Astro term(Astro functor, Object args) {
-        return new QTerm(myBuilder, (QAstro)functor, ((EList)args).snapshot());
+        return new QTerm(myBuilder,
+                         (QAstro)functor,
+                         ((EList)args).snapshot());
     }
 
+    /**
+     * Just returns 'functor' itself.
+     * <p>
+     * Note that, if 'functor' is a term-hole (one that doesn't constrain
+     * the literal term to be zero-arity), then so will the result, since it's
+     * the same.
+     */
     public Astro term(Astro functor) {
-        throw new RuntimeException("XXX not yet implemented");
+        return functor;
     }
 
     /**
@@ -128,7 +146,7 @@
     }
 
     public Astro taggedHole(Astro ident, Astro functorHole) {
-        throw new RuntimeException("XXX not yet implemented");
+        return ((QHole)functorHole).asTagged(ident);
     }
 
     /**
@@ -148,8 +166,18 @@
     /**
      *
      */
-    public AstroArg seq(Astro optTerm, String quant) {
-        throw new RuntimeException("XXX not yet implemented");
+    public AstroArg some(Astro optTerm, String quant) {
+        T.require(quant.length() == 1, "Must be single char: ", quant);
+        Twine source;
+        if (null == optTerm) {
+            source = EmptyTwine.THE_ONE;
+        } else {
+            source = optTerm.getSource();
+        }
+        return new QSome(myBuilder,
+                         (QAstroArg)optTerm,
+                         quant.charAt(0),
+                         source);
     }
 
     /**
@@ -160,16 +188,29 @@
     }
 
     /**
-     *
+     * The dollar-hole that's returned is initially a term-hole, but may get
+     * converted to a functor-hole by {@link QAstro#asFunctor()}.
      */
     public Astro dollarHole(Astro litInt) {
-        throw new RuntimeException("XXX not yet implemented");
+        int holeNum = ((BigInteger)litInt.getOptData()).intValue();
+        return new QDollarHole(myBuilder,
+                               null,
+                               holeNum,
+                               false,
+                               litInt.getSource());
     }
 
     /**
      *
+     * The at-hole that's returned is initially a term-hole, but may get
+     * converted to a functor-hole by {@link QAstro#asFunctor()}.
      */
     public Astro atHole(Astro litInt) {
-        throw new RuntimeException("XXX not yet implemented");
+        int holeNum = ((BigInteger)litInt.getOptData()).intValue();
+        return new QAtHole(myBuilder,
+                           null,
+                           holeNum,
+                           false,
+                           litInt.getSource());
     }
 }



1.4       +39 -10    e/src/jsrc/org/quasiliteral/quasiterm/QTerm.java

Index: QTerm.java
===================================================================
RCS file: /cvs/e/src/jsrc/org/quasiliteral/quasiterm/QTerm.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- QTerm.java	2001/12/19 22:36:13	1.3
+++ QTerm.java	2001/12/21 05:30:35	1.4
@@ -63,7 +63,7 @@
           ConstList qArgs) {
 
         super(builder, qFunctor.getSource());
-        myQFunctor = qFunctor;
+        myQFunctor = qFunctor.asFunctor();
         myQArgs = qArgs;
     }
 
@@ -152,12 +152,12 @@
      *         literal functor info of this qterm, and whose args are the
      *         concatentation of the substSlice of the qargs of this qterm.
      */
-    public ConstList substSlice(Object[] args, String rank) {
-        Astro tFunctor = (Astro)myQFunctor.substSlice(args, rank).get(0);
+    public ConstList substSlice(Object[] args, int[] index) {
+        Astro tFunctor = (Astro)myQFunctor.substSlice(args, index).get(0);
         Object tArgs = myBuilder.list();
         for (int i = 0, ilen = myQArgs.size(); i < ilen; i++) {
             QAstroArg qArg = (QAstroArg)myQArgs.get(i);
-            ConstList tSlice = qArg.substSlice(args, rank);
+            ConstList tSlice = qArg.substSlice(args, index);
             for (int j = 0, jlen = tSlice.size(); j < jlen; j++) {
                 AstroArg tArg = (AstroArg)tSlice.get(j);
                 tArgs = myBuilder.with(tArgs, tArg);
@@ -178,23 +178,26 @@
     public int matchBindSlice(Object[] args,
                               ConstList specimenList,
                               FlexList bindings,
-                              String rank) {
+                              int[] index) {
+        Astro specimen = (Astro)specimenList.get(0);
+        ConstList singletonFunctorList =
+          ConstList.EmptyList.with(specimen.withoutArgs());
         int matches = myQFunctor.matchBindSlice(args,
-                                                specimenList,
+                                                singletonFunctorList,
                                                 bindings,
-                                                rank);
+                                                index);
         if (matches <= 0) {
             return -1;
         }
         T.requireSI(1 == matches,
                     "Functor may only match 0 or 1 specimen: ", matches);
-        ConstList tArgs = ((Astro)specimenList.get(0)).getArgs();
+        ConstList tArgs = specimen.getArgs();
         for (int i = 0, len = myQArgs.size(); i < len; i++) {
             QAstroArg qArg = (QAstroArg)myQArgs.get(i);
             int num = qArg.matchBindSlice(args,
                                           tArgs,
                                           bindings,
-                                          rank);
+                                          index);
             if (num <= -1) {
                 return -1;
             }
@@ -226,7 +229,7 @@
      */
     public void prettyPrintOn(TextWriter out)
       throws IOException {
-        String label = myQFunctor.asText();
+        String label = myQFunctor.toString();
         out.print(label);
         int h = getHeight();
         if (h <= 1) {
@@ -259,5 +262,31 @@
             ((QAstroArg)myQArgs.get(i)).prettyPrintOn(sub);
         }
         sub.print(")");
+    }
+
+    /**
+     *
+     */
+    /*package*/
+    QAstro asFunctor() {
+        T.require(myQArgs.size() == 0,
+                  "Terms with args can't be used as functors: ", this);
+        //could 'return this;', but may as well shed unnecessary structure
+        return myQFunctor;
+    }
+
+    /**
+     * A QTerm has whatever shape its children agree on
+     */
+    /*package*/
+    int getShape(Object args[],
+                 int[] prefix,
+                 int shapeSoFar) {
+        shapeSoFar = myQFunctor.getShape(args, prefix, shapeSoFar);
+        for (int i = 0, len = myQArgs.size(); i < len; i++) {
+            QAstroArg qArg = (QAstroArg)myQArgs.get(i);
+            shapeSoFar = qArg.getShape(args, prefix, shapeSoFar);
+        }
+        return shapeSoFar;
     }
 }



1.1                  e/src/jsrc/org/quasiliteral/quasiterm/QAstro.java

Index: QAstro.java
===================================================================
package org.quasiliteral.quasiterm;

import org.erights.e.develop.assertion.T;
import org.erights.e.elib.tables.Twine;
import org.quasiliteral.astro.Astro;
import org.quasiliteral.astro.AstroBuilder;
import org.quasiliteral.astro.AstroTag;

//This file is hereby placed in the public domain

/**
 *
 * @author <a href="mailto:markm@caplet.com">Mark Miller</a>
 */
public abstract class QAstro extends QAstroArg implements Astro {

    public QAstro(AstroBuilder builder, Twine source) {
        super(builder, source);
    }

    /**
     *
     */
    public Astro build(AstroBuilder builder) {
        throw new RuntimeException("Can't build.  Try qbuild instead");
    }

    /**
     *
     */
    public short getOptTagCode() {
        return getTag().getOptTagCode();
    }

    /**
     *
     */
    public String getOptString() {
        return ((Twine)getOptData()).bare();
    }

    /**
     *
     */
    public Object getOptArgData() {
        if (getArgs().size() != 1) {
            return null;
        }
        QAstroArg qaa = (QAstroArg)getArgs().get(0);
        if (!(qaa instanceof QAstro)) {
            return null;
        }
        return ((QAstro)qaa).getOptData();
    }

    /**
     *
     */
    public Object getOptArgData(short tagCode) {
        //This is too much reporting work when the error doesn't happen.
        //Should consider putting into a thunk instead.
        AstroTag optTag = getTag().getSchema().getOptTagForCode(tagCode);
        T.require(tagCode == getTag().getOptTagCode(),
                  "Tag mismatch: ", getTag(), " vs ",
                  (null == optTag ?
                   (Object)new Integer(tagCode) :
                   (Object)optTag));
        return getOptArgData();
    }

    /**
     *
     */
    public String getOptArgString(short tagCode) {
        return ((Twine)getOptArgData(tagCode)).bare();
    }

    /**
     * Returns a variant of this Astro that may serve as a functor of a
     * QTerm.
     * <p>
     * If this Astro (or derivatives) should not be used as a functor, then
     * this operation should throw.
     */
    /*package*/ abstract QAstro asFunctor();
}



1.1                  e/src/jsrc/org/quasiliteral/quasiterm/QAtHole.java

Index: QAtHole.java
===================================================================
package org.quasiliteral.quasiterm;

import org.erights.e.develop.format.StringHelper;
import org.erights.e.develop.assertion.T;
import org.erights.e.develop.exception.ExceptionMgr;
import org.erights.e.elib.eio.TextWriter;
import org.erights.e.elib.prim.E;
import org.erights.e.elib.prim.StaticMaker;
import org.erights.e.elib.tables.ConstList;
import org.erights.e.elib.tables.FlexList;
import org.erights.e.elib.tables.Twine;
import org.erights.e.elib.tables.EList;
import org.erights.e.elib.base.Ejector;
import org.quasiliteral.astro.Astro;
import org.quasiliteral.astro.AstroArg;
import org.quasiliteral.astro.AstroBuilder;
import org.quasiliteral.astro.AstroTag;
import org.quasiliteral.term.QuasiBuilder;

import java.io.IOException;
import java.math.BigInteger;

//This file is hereby placed in the public domain

/**
 * An at-hole of a quasi-literal term expression extracts the specimen into
 * a binding.
 * <p>
 * An at-hole is not a valid ValueMaker, and so neither is any quasi tree that
 * contains an at-hole.  If encountered during substitution, an at-hole
 * throws.  It would be good to make this error occur earlier.
 * <p>
 * As a MatchMaker, this requires the specimen to meet its constraints
 * (optional tag, optional requirement of zero arity), and then extracts it
 * into the binding at [hole-number]+index.
 *
 * @author <a href="mailto:markm@caplet.com">Mark S Miller</a>
 */
public class QAtHole extends QHole {

    /**
     *
     */
    static public final StaticMaker QAtHoleMaker =
      StaticMaker.make(QAtHole.class);

    /**
     * Makes a hole that extracts a specimen into a binding.
     * <p>
     * The invariants of a QAtHole are not checked here, but rather are
     * enforced by the callers in this class and in QTermBuilder.
     * <p>
     * For the meanings of the parameters, see the {@link QHole} constructor,
     * which has the same parameters.
     */
    /*package*/
    QAtHole(AstroBuilder builder,
            AstroTag optTag,
            int holeNum,
            boolean isFunctorHole,
            Twine source) {

        super(builder, optTag, holeNum, isFunctorHole, source);
    }

    /**
     * Uses 'QAtHoleMaker new(myBuilder, myOptTag, myHoleNum,
     * myIsFunctorHole, mySource)'
     */
    public Object[] getCanonicalState() {
        Object[] result = {
            QAtHoleMaker,
            "new",
            myBuilder,
            myOptTag,
            BigInteger.valueOf(myHoleNum),
            myIsFunctorHole ? Boolean.TRUE : Boolean.FALSE,
            mySource
        };
        return result;
    }

    /**
     *
     */
    public AstroArg qbuild(QuasiBuilder qbuilder) {
        Astro holeNum = qbuilder.leafLong(myHoleNum, mySource);
        Astro result = qbuilder.atHole(holeNum);
        if (null != myOptTag) {
            result = qbuilder.taggedHole(this, result);
        }
        if (myIsFunctorHole) {
            result = qbuilder.term(result, qbuilder.list());
        }
        return result;
    }

    /**
     * An at-hole doesn't contribute to the shape, so just returns
     * shapeSoFar.
     */
    /*package*/
    int getShape(Object[] args, int[] prefix, int shapeSoFar) {
        return shapeSoFar;
    }

    /**
     * This throws, complaining that a quasi-tree with an @-hole may not be
     * used as a ValueMaker.
     */
    public ConstList substSlice(Object[] args, int[] index) {
        throw new RuntimeException
          ("A quasi-tree with an @-hole may not be used as a ValueMaker: " +
           this);
    }

    /**
     * Given a multi-dimensional list and an index path, put newValue at that
     * position in the list.
     * <p>
     * For example, if 'bindings' is [['a','b'],['c','d','e']] diverge(),
     * 'holeNum' is 1, 'index' is [2], and 'newValue' is 'x', then the 'e'
     * should be replaced with 'x', since it's at list[1][2].  If any index
     * step is out of bounds, the corresponding list is grown to include it
     * (see {@link FlexList#ensureSize(int)} and null is returned.
     * Alternatively, if an old value is being overwritten, then that old
     * value is also returned.
     */
    static public Object multiPut(FlexList bindings,
                                  int holeNum,
                                  int[] index,
                                  Object newValue) {
        FlexList list = bindings;
        int dest = holeNum;
        for (int i = 0, len = index.length; i < len; i++) {
            list.ensureSize(dest +1);
            Object optNext = list.get(dest);
            if (optNext == null) {
                optNext = FlexList.make(index[i] +1);
                list.put(dest, optNext);
            } else if (optNext instanceof FlexList) {
                //we're cool
            } else {
                optNext = EListGuard.coerce(optNext, null);
                optNext = ((EList)optNext).diverge();
            }
            list = (FlexList)optNext;
            dest = index[i];
        }
        Object result = null;
        if (list.size() > dest) {
            result = list.get(dest);
        }
        list.ensureSize(dest +1);
        list.put(dest, newValue);
        return result;
    }

    /**
     * This extracts the specimen into the binding at [myHoleNum]+index.
     * <p>
     * This first ensures the specimen meets our own constraints.
     *
     * @return -1 or 1, depending on whether they match.
     */
    public int matchBindSlice(Object[] args,
                              ConstList specimenList,
                              FlexList bindings,
                              int[] index) {
        if (specimenList.size() <= 0) {
            return -1;
        }
        Object specimen = specimenList.get(0);
        if (! matches(specimen)) {
            return -1;
        }
        //XXX when matches/1 coerces term to an Astro, this should be updated
        //to return a singleton list of the coerced term rather than the
        //pre-coerced term.
        Object optOldValue = multiPut(bindings, myHoleNum, index, specimen);
        if (null == optOldValue ||
          0.0 == E.asFloat64(E.call(optOldValue, "compareTo", specimen))) {

            return 1;
        } else {
            //XXX Should this throw?
            return -1;
        }
    }

    /**
     *
     */
    /*package*/
    QHole asTagged(Astro ident) {
        T.require(null == myOptTag,
                  "Already tagged: ", this);
        return new QAtHole(myBuilder,
                           ident.getTag(),
                           myHoleNum,
                           myIsFunctorHole,
                           mySource);
    }

    /**
     *
     */
    /*package*/
    QAstro asFunctor() {
        if (myIsFunctorHole) {
            return this;
        } else {
            return new QAtHole(myBuilder,
                               myOptTag,
                               myHoleNum,
                               true,
                               mySource);
        }
    }

    /**
     *
     */
    public void prettyPrintOn(TextWriter out)
      throws IOException {
        if (null != myOptTag) {
            out.print(myOptTag.getTagName());
        }
        out.print("@{", new Integer(myHoleNum), "}");
    }
}



1.1                  e/src/jsrc/org/quasiliteral/quasiterm/QDollarHole.java

Index: QDollarHole.java
===================================================================
package org.quasiliteral.quasiterm;

import org.erights.e.develop.format.StringHelper;
import org.erights.e.develop.assertion.T;
import org.erights.e.develop.exception.ExceptionMgr;
import org.erights.e.elib.eio.TextWriter;
import org.erights.e.elib.prim.E;
import org.erights.e.elib.prim.StaticMaker;
import org.erights.e.elib.tables.ConstList;
import org.erights.e.elib.tables.FlexList;
import org.erights.e.elib.tables.Twine;
import org.erights.e.elib.tables.EList;
import org.erights.e.elib.base.Ejector;
import org.quasiliteral.astro.Astro;
import org.quasiliteral.astro.AstroArg;
import org.quasiliteral.astro.AstroBuilder;
import org.quasiliteral.astro.AstroTag;
import org.quasiliteral.term.QuasiBuilder;

import java.io.IOException;
import java.math.BigInteger;

//This file is hereby placed in the public domain

/**
 * A dollar hole of a quasi-literal term expression is filled in with a
 * substitution arg.
 * <p>
 * As a ValueMaker, this evaluates to the corresponding substitution arg,
 * given that it meets all encoded conditions (see {@link QHole}).
 * <p>
 * As a MatchMaker, this compares ("<=>") a specimen term against
 * the substitution argument.  The substitution argument must again meet all
 * encoded conditions.
 *
 * @author <a href="mailto:markm@caplet.com">Mark S Miller</a>
 */
public class QDollarHole extends QHole {

    /**
     *
     */
    static public final StaticMaker QDollarHoleMaker =
      StaticMaker.make(QDollarHole.class);

    /**
     * Makes a hole that is filled in by a substitution arg.
     * <p>
     * The invariants of a QDollarHole are not checked here, but rather are
     * enforced by the callers in this class and in QTermBuilder.
     * <p>
     * For the meanings of the parameters, see the {@link QHole} constructor,
     * which has the same parameters.
     */
    /*package*/
    QDollarHole(AstroBuilder builder,
                AstroTag optTag,
                int holeNum,
                boolean isFunctorHole,
                Twine source) {

        super(builder, optTag, holeNum, isFunctorHole, source);
    }

    /**
     * Uses 'QDollarHoleMaker new(myBuilder, myOptTag, myHoleNum,
     * myIsFunctorHole, mySource)'
     */
    public Object[] getCanonicalState() {
        Object[] result = {
            QDollarHoleMaker,
            "new",
            myBuilder,
            myOptTag,
            BigInteger.valueOf(myHoleNum),
            myIsFunctorHole ? Boolean.TRUE : Boolean.FALSE,
            mySource
        };
        return result;
    }

    /**
     *
     */
    public AstroArg qbuild(QuasiBuilder qbuilder) {
        Astro holeNum = qbuilder.leafLong(myHoleNum, mySource);
        Astro result = qbuilder.dollarHole(holeNum);
        if (null != myOptTag) {
            result = qbuilder.taggedHole(this, result);
        }
        if (myIsFunctorHole) {
            result = qbuilder.term(result, qbuilder.list());
        }
        return result;
    }

    /**
     * Given a multi-dimensional list and an index path, retrieve the
     * corresponding element of the list.
     * <p>
     * For example, if 'args' is [['a','b'],['c','d','e']], 'holeNum' is 1,
     * 'index' is [2,3], and 'repeat' is true, then the answer should be
     * 'e', since it's at args[1][2], and the repeat flag allows us to ignore
     * the 3 when we find that 'e' isn't a list.  If 'repeat' had been false,
     * the presence of an additional step on the index path would have caused
     * an exception to be thrown.  In either case, if an index step is out of
     * bounds, an exception is thrown regardless of the value of 'repeat'.
     */
    static public Object multiGet(Object args[],
                                  int holeNum,
                                  int[] index,
                                  boolean repeat) {
        Object result = args[holeNum];
        for (int i = 0, len = index.length; i < len; i++) {
            Ejector optEj = null;
            if (repeat) {
                optEj = new Ejector();
            }
            EList list;
            try {
                list = (EList)EListGuard.coerce(result, optEj);
            } catch (Throwable th) {
                if (null == optEj) {
                    throw ExceptionMgr.asSafe(th);
                } else {
                    optEj.result(th);
                    //It doesn't matter why the coercion failed.  If we're
                    //here, the coercion failed rather than throwing.  This
                    //means we should simply repeat the last non-list result
                    //we got.
                    return result;
                }
            }
            result = list.get(index[i]);
        }
        return result;
    }

    /**
     * If the substitution arg at [myHoleHum]+prefix is actually a list
     * for further indexing, what's the size of that list?
     * <p>
     * If it's not a list, it could still act as a list by repetition,
     * but then it's of indeterminate size, so just return shapeSoFar.
     * If it is a list, then if shapeSoFar has already been determined (ie,
     * not -1), then require these to agree.
     */
    /*package*/
    int getShape(Object[] args, int[] prefix, int shapeSoFar) {
        Object term = multiGet(args, myHoleNum, prefix, true);
        Ejector ej = new Ejector();
        EList list;
        try {
            list = (EList)EListGuard.coerce(term, ej);
        } catch (Throwable th) {
            ej.result(th);
            //It doesn't matter why the coercion failed.  If we're
            //here, the coercion failed rather than throwing.
            return shapeSoFar;
        }
        int result = list.size();
        T.require(-1 == shapeSoFar || shapeSoFar == result,
                  "Inconsistent shape: " + shapeSoFar,
                  " vs " + result);
        return result;
    }

    /**
     * This extracts the substitution arg at [myHoleNum]+index, requires that
     * it matches, and returns a singleton list containing that arg (which
     * should be a literal term).
     */
    public ConstList substSlice(Object[] args, int[] index) {
        Object term = multiGet(args, myHoleNum, index, true);
        T.require(matches(term),
                  "Term ", term, " doesn't match ", this);
        //XXX when matches/1 coerces term to an Astro, this should be updated
        //to return a singleton list of the coerced term rather than the
        //pre-coerced term.
        return ConstList.EmptyList.with(term);
    }

    /**
     * This compares ("<=>") the substitution arg at [myHoleNum]+index against
     * the specimenList[0].
     * <p>
     * This first ensures that the substitution arg meets our own constraints.
     *
     * @return -1 or 1, depending on whether they match.
     */
    public int matchBindSlice(Object[] args,
                              ConstList specimenList,
                              FlexList bindings,
                              int[] index) {
        if (specimenList.size() <= 0) {
            return -1;
        }
        Object specimen = specimenList.get(0);
        Object term = multiGet(args, myHoleNum, index, true);
        T.require(matches(term),
                  "Term ", term, " doesn't match ", this);
        //XXX when matches/1 coerces term to an Astro, this should be updated
        //to return a singleton list of the coerced term rather than the
        //pre-coerced term.
        if (0.0 == E.asFloat64(E.call(term, "compareTo", specimen))) {
            return 1;
        } else {
            return -1;
        }
    }

    /**
     *
     */
    /*package*/
    QHole asTagged(Astro ident) {
        T.require(null == myOptTag,
                  "Already tagged: ", this);
        return new QDollarHole(myBuilder,
                               ident.getTag(),
                               myHoleNum,
                               myIsFunctorHole,
                               mySource);
    }

    /**
     *
     */
    /*package*/
    QAstro asFunctor() {
        if (myIsFunctorHole) {
            return this;
        } else {
            return new QDollarHole(myBuilder,
                                   myOptTag,
                                   myHoleNum,
                                   true,
                                   mySource);
        }
    }

    /**
     *
     */
    public void prettyPrintOn(TextWriter out)
      throws IOException {
        if (null != myOptTag) {
            out.print(myOptTag.getTagName());
        }
        out.print("${", new Integer(myHoleNum), "}");
    }
}



1.1                  e/src/jsrc/org/quasiliteral/quasiterm/QFunctor.java

Index: QFunctor.java
===================================================================
package org.quasiliteral.quasiterm;

import org.erights.e.develop.format.StringHelper;
import org.erights.e.elib.eio.TextWriter;
import org.erights.e.elib.prim.E;
import org.erights.e.elib.prim.StaticMaker;
import org.erights.e.elib.tables.ConstList;
import org.erights.e.elib.tables.FlexList;
import org.erights.e.elib.tables.Twine;
import org.quasiliteral.astro.Astro;
import org.quasiliteral.astro.AstroArg;
import org.quasiliteral.astro.AstroBuilder;
import org.quasiliteral.astro.AstroTag;
import org.quasiliteral.term.QuasiBuilder;

import java.io.IOException;

//This file is hereby placed in the public domain

/**
 * A quasi-literal functor of a {@link Term}.
 * <p>
 * As a ValueMaker, this acts like a 0-arity Term.  As a MatchMaker, this
 * matches only the functor info of a specimen term, and ignores the
 * specimen's arguments.
 *
 * @author <a href="mailto:markm@caplet.com">Mark S Miller</a>
 */
public class QFunctor extends QAstro {

    /**
     *
     */
    static public final StaticMaker QFunctorMaker =
      StaticMaker.make(QFunctor.class);

    /**
     * @serial Represents the token-type of the functor of this QTerm.
     */
    private final AstroTag myTag;

    /**
     * @serial If the functor represents a literal-data token, then this is
     * the data, and myTag must represent the cononical corresponding
     * token-type for this kind of data in this schema.
     */
    private final Object myOptData;

    /**
     * Makes a QTerm that matches or generates a Astro.
     * <p>
     * The invariants of a QTerm are not checked here, but rather are
     * enforced by the callers in this class and in QTermBuilder.
     *
     * @param builder Used to build the result of a substitute
     * @param tag Identifies a token type in a particular grammar or set
     *            of related grammars, used as the functor (or "label") of
     *            this QTerm
     * @param optData Either {@link Character},
     *                {@link BigInteger}, {@link Double},
     *                or {@link Twine} or null.  If not null, then the tag
     *                must represent the canonical literal type for this
     *                kind of data in this schema.
     * @param source The source text this node was extracted from.  To
     *               provide no info, use "" rather than null.
     */
    /*package*/
    QFunctor(AstroBuilder builder,
             AstroTag tag,
             Object optData,
             Twine source) {

        super(builder, source);
        myTag = tag;
        myOptData = optData;
    }

    /**
     * Uses 'QFunctorMaker new(myBuilder, myTag, myOptData, mySource)'
     */
    public Object[] getCanonicalState() {
        Object[] result = {
            QFunctorMaker, "new", myBuilder, myTag, myOptData, mySource
        };
        return result;
    }

    /**
     *
     */
    public AstroArg qbuild(QuasiBuilder qbuilder) {
        if (null == myOptData) {
            return qbuilder.leafTag(myTag, mySource);
        } else {
            //Assumes tag adds no more info.
            return qbuilder.leafData(myOptData, mySource);
        }
    }

    /**
     * Represents the token-type of the functor of this term.
     */
    public AstroTag getTag() {
        return myTag;
    }

    /**
     * Either literal data or null.  If not null, then the tag
     * must represent the canonical literal type for this
     * kind of data in this schema.
     */
    public Object getOptData() {
        return myOptData;
    }

    /**
     * @return An empty list of QAstroArg
     */
    public ConstList getArgs() {
        return ConstList.EmptyList;
    }

    /**
     *
     */
    public Astro withoutArgs() {
        return this;
    }

    /**
     *
     */
    public Astro withArgs(ConstList qArgs) {
        return new QTerm(myBuilder, this, qArgs);
    }

    /**
     * @return A single list of a single Astro, whose functor is based on
     *         literal functor info of this qfunctor
     */
    public ConstList substSlice(Object[] args, int[] index) {
        Astro tFunctor;
        if (null == myOptData) {
            tFunctor = myBuilder.leafTag(myTag, mySource);
        } else {
            tFunctor = myBuilder.leafData(myOptData, mySource);
        }
        return ConstList.EmptyList.with(tFunctor);
    }

    /**
     * Attempts to match against the Astro specimenList[0].
     * <p>
     * @return -1 or 1, depending on whether the functor information of
     *         specimenList[0] matches that of this qfunctor, while ignoring
     *         the args.
     */
    public int matchBindSlice(Object[] args,
                              ConstList specimenList,
                              FlexList bindings,
                              int[] index) {
        if (specimenList.size() <= 0) {
            return -1;
        }
        Astro term = (Astro)specimenList.get(0);
        if (0.0 != myTag.compareTo(term.getTag())) {
            return -1;
        }
        if (null != myOptData) {
            //if myOptData is null, then it's a wildcard.
            Object optOtherData = term.getOptData();
            if (null == optOtherData) {
                //If the pattern has data, then the specimen must as well
                return -1;
            }
            if (!myOptData.equals(optOtherData)) {
                //'equals/1' is valid for all valid literal data types except
                //Twine
                if (myOptData instanceof Twine &&
                  optOtherData instanceof Twine) {

                    String mine = ((Twine)myOptData).bare();
                    String other = ((Twine)optOtherData).bare();
                    if (!mine.equals(other)) {
                        return -1;
                    }
                }
            }
        }
        //functor info matches, so we match one (the first) element of
        //specimenList
        return 1;
    }

    /**
     * A functor as a Term is a leaf, and so has height 1
     */
    public int getHeight() {
        return 1;
    }

    /**
     *
     */
    public void prettyPrintOn(TextWriter out)
      throws IOException {
        String label = myTag.getTagName();
        if (null != myOptData) {
            label = E.toQuote(myOptData).bare();
            label = StringHelper.replaceAll(label, "$", "$$");
            label = StringHelper.replaceAll(label, "@", "@@");
            label = StringHelper.replaceAll(label, "`", "``");
        }
        out.print(label);
    }

    /*package*/
    QAstro asFunctor() {
        return this;
    }

    /**
     * Just returns shapeSoFar, since this has no shape and no children
     */
    /*package*/
    int getShape(Object args[],
                 int[] prefix,
                 int shapeSoFar) {
        return shapeSoFar;
    }
}



1.1                  e/src/jsrc/org/quasiliteral/quasiterm/QHole.java

Index: QHole.java
===================================================================
package org.quasiliteral.quasiterm;

import org.quasiliteral.astro.AstroBuilder;
import org.quasiliteral.astro.AstroTag;
import org.quasiliteral.astro.Astro;
import org.erights.e.elib.tables.Twine;
import org.erights.e.elib.tables.ConstList;
import org.erights.e.develop.assertion.T;

//This file is hereby placed in the public domain

/**
 * Represents a dollar-hole ("${<hole-num>}") or an at-hole ("@{<hole-num>}")
 * that may be a functor-hole or a term-hole, and that may or may not insist
 * on a tag.
 *
 * @author <a href="mailto:markm@caplet.com">Mark Miller</a>
 */
public abstract class QHole extends QAstro {

    /**
     * @serial If present, represents the token tag that the corresponding
     *         literal term must have, and this is a tagged-hole.  If this is
     *         a dollar-hole, the corresponding literal term must be the
     *         substitution are at myHoleNum.  If this is an at-hole, the
     *         corresponding literal term is the specimen.
     */
    /*package*/ final AstroTag myOptTag;

    /**
     * @serial Which hole am I?  If this is a dollar-hole, then this
     *         says which substitution-arg.  If this is an at-hole, then this
     *         says which binding.
     */
    /*package*/ final int myHoleNum;

    /**
     * @serial If true, then the corresponding literal term must be
     *         0-arity.  If false, then the corresponding literal term may
     *         itself have any argument list, and this is a termHole
     */
    /*package*/ final boolean myIsFunctorHole;

    /**
     * Makes a hole that matches a term (either a substitution-arg or a
     * specimen), and either evaluates to it (if this is a dollar-hole) or
     * extracts it (if this is an at-hole).
     *
     * @param builder Used to build the result of a substitute.
     * @param optTag If present, represents the token tag that the
     *               corresponding literal term must have, and this is a
     *               tagged-hole.  If this is a dollar-hole, the corresponding
     *               literal term must be the substitution are at myHoleNum.
     *               If this is an at-hole, the corresponding literal term is
     *               the specimen.
     * @param holeNum Which hole am I?  If this is a dollar-hole, then this
     *                says which substitution-arg.  If this is an at-hole,
     *                then this says which binding.
     * @param isFunctorHole If true, then the corresponding literal term must
     *                      be 0-arity.  If false, then the corresponding
     *                      literal term may itself have any argument list,
     *                      and this is a termHole.
     * @param source The source text this node was extracted from.  To
     *               provide no info, use "" rather than null.
     */
    /*package*/
    QHole(AstroBuilder builder,
          AstroTag optTag,
          int holeNum,
          boolean isFunctorHole,
          Twine source) {

        super(builder, source);
        myOptTag = optTag;
        myHoleNum = holeNum;
        myIsFunctorHole = isFunctorHole;
    }

    /**
     * Does the literal 'term' match the pattern represented by this hole?
     * <p>
     * When this hole is a dollar-hole, term will be a substitution-arg.
     * When this hole is an at-hole, term will be the specimen.  In either
     * case, the same criteria are applied: <ul>
     * <li>If term isn't an Astro (or, XXX eventually, coerces to an Astro),
     *     then false -- we have no match</li>
     * <li>If we have a tag, and it doesn't match term's tag, then false.</li>
     * <li>If we are a functor-hole (rather than a term-hole) and term has
     *     one or more arguments, then false.</li>
     * <li>Otherwise, we match, so true.</li>
     * </ul>
     */
    /*package*/
    boolean matches(Object term) {
        if (! (term instanceof Astro)) {
            return false;
        }
        Astro astro = (Astro)term;
        if (null != myOptTag && 0.0 != myOptTag.compareTo(astro.getTag())) {
            return false;
        }
        if (myIsFunctorHole && astro.getArgs().size() != 0) {
            return false;
        }
        return true;
    }

    /**
     * Tagged holes return their tag, otherwise throws.
     */
    public AstroTag getTag() {
        T.require(null != myOptTag,
                  "There ain't no tag on an untagged hole");
        return myOptTag;
    }

    /**
     * Holes have no data, so getOptData/0 always returns null.
     */
    public Object getOptData() {
        return null;
    }

    /**
     * A hole itself has no args, even though a term-hole will match a
     * corresponding literal term that does.
     *
     * @return An empty list of QAstroAr
     */
    public ConstList getArgs() {
        return ConstList.EmptyList;
    }

    /**
     * A hole itself has no args, so this just returns itself.
     */
    public Astro withoutArgs() {
        return this;
    }

    /**
     * Can only do this to a functor-hole, in which case it makes a
     * {@link QTerm}.
     */
    public Astro withArgs(ConstList qArgs) {
        T.require(myIsFunctorHole,
                  "Can only add args to a functor-hole, not a term-hole: ",
                  this);
        return new QTerm(myBuilder, this, qArgs);
    }

    /**
     * A hole is a leaf, and so has height 1
     */
    public int getHeight() {
        return 1;
    }

    /**
     *
     */
    /*package*/ abstract QHole asTagged(Astro ident);
}



1.1                  e/src/jsrc/org/quasiliteral/quasiterm/QSome.java

Index: QSome.java
===================================================================
package org.quasiliteral.quasiterm;

import org.erights.e.elib.tables.ConstList;
import org.erights.e.elib.tables.FlexList;
import org.erights.e.elib.tables.Twine;
import org.erights.e.elib.tables.EList;
import org.erights.e.elib.eio.TextWriter;
import org.erights.e.elib.prim.StaticMaker;
import org.erights.e.develop.assertion.T;
import org.quasiliteral.astro.AstroTag;
import org.quasiliteral.astro.AstroArg;
import org.quasiliteral.astro.Astro;
import org.quasiliteral.astro.AstroBuilder;
import org.quasiliteral.term.QuasiBuilder;

import java.io.IOException;

//This file is hereby placed in the public domain

/**
 *
 * @author <a href="mailto:markm@caplet.com">Mark Miller</a>
 */
public class QSome extends QAstroArg {

    /**
     *
     */
    static public StaticMaker QSomeMaker =
      StaticMaker.make(QSome.class);

    /**
     *
     */
    private final QAstroArg myOptSubPattern;

    /**
     *
     */
    private final char myQuant;

    /**
     *
     * @param quant One of '?', '+', or '*'.
     */
    public QSome(AstroBuilder builder,
                 QAstroArg optSubPattern,
                 char quant,
                 Twine source) {
        super(builder, source);
        myOptSubPattern = optSubPattern;
        myQuant = quant;
    }

    /**
     * Uses 'QSomeMaker new(myBuilder, myOptSubPattern, myQuant, mySource)'
     */
    public Object[] getCanonicalState() {
        Object[] result = {
            QSomeMaker,
            "new",
            myBuilder,
            myOptSubPattern,
            new Character(myQuant),
            mySource
        };
        return result;
    }

    /**
     *
     */
    static private boolean inBounds(int num, char quant) {
        switch (quant) {
            case '?': return num == 0 || num == 1;
            case '+': return num >= 1;
            case '*': return num >= 0;
            default: throw new RuntimeException
              ("Must be '?', '+', or '*': " + quant);
        }
    }

    /**
     *
     */
    public ConstList substSlice(Object[] args, int[] index) {
        T.require(null != myOptSubPattern,
                  "A ValueMaker must have a sub-pattern: ", this);
        int shape = myOptSubPattern.getShape(args, index, -1);
        T.require(shape >= 0,
                  "Indeterminate repetition: ", this);
        FlexList result = FlexList.fromType(Astro.class, shape);
        int lastDim = index.length;
        int[] subIndex = (int[])EList.resize(index, lastDim +1);
        for (int i = 0; i < shape; i++) {
            subIndex[lastDim] = i;
            ConstList slice = myOptSubPattern.substSlice(args, subIndex);
            result.append(slice);
        }
        T.require(inBounds(result.size(), myQuant),
                  "Improper quantity: " + shape, " vs " + myQuant);
        return result.snapshot();
    }

    /**
     *
     */
    public int matchBindSlice(Object[] args,
                              ConstList specimenList,
                              FlexList bindings,
                              int[] index) {
        if (null == myOptSubPattern) {
            int result = specimenList.size();
            switch (myQuant) {
                case '?': return Math.min(result, 1);
                case '+': return result <= 0 ? -1 : result;
                case '*': return result;
                default: T.fail("Unrecognized: " + myQuant);
            }
            return -666; //just so the compiler will know
        }
        int shape = myOptSubPattern.getShape(args, index, -1);
        int result = 0;
        int lastDim = index.length;
        int[] subIndex = (int[])EList.resize(index, lastDim +1);
        for (int i = 0; shape == -1 || i < shape; i++) {
            if (specimenList.size() == 0) {
                break;
            }
            if (myQuant == '?' && result >= 1) {
                break;
            }
            subIndex[lastDim] = i;
            int more = myOptSubPattern.matchBindSlice(args,
                                                      specimenList,
                                                      bindings,
                                                      subIndex);
            if (-1 == more) {
                break;
            }
            T.require(more >= 1 || shape != -1,
                      "Patterns of indeterminate rank must make progress: ",
                      this, " vs ", specimenList);
            result += more;
            specimenList = specimenList.run(more, specimenList.size());
        }
        T.require(inBounds(result, myQuant),
                  "Improper quantity: " + result, " vs " + myQuant);
        return result;
    }

    /**
     *
     */
    /*package*/
    int getShape(Object args[],
                 int[] prefix,
                 int shapeSoFar) {
        throw new RuntimeException("XXX not yet implemented");
    }

    /**
     *
     */
    public AstroArg qbuild(QuasiBuilder qbuilder) {
        //XXX what if myOptSubPattern is only an AstroArg, not an Astro?
        return qbuilder.some((Astro)myOptSubPattern, "" + myQuant);
    }

    /**
     * If I have a sub-pattern, my height is the same as its.
     * <p>
     * Otherwise, my height is 1, which is the height of a leaf.
     */
    public int getHeight() {
        if (null == myOptSubPattern) {
            return 1;
        } else {
            return myOptSubPattern.getHeight();
        }
    }

    /**
     *
     */
    public void prettyPrintOn(TextWriter out)
      throws IOException {
        if (null != myOptSubPattern) {
            myOptSubPattern.prettyPrintOn(out);
        }
        out.print("" + myQuant);
    }
}



1.4       +3 -2      e/src/jsrc/org/quasiliteral/term/QuasiBuilder.java

Index: QuasiBuilder.java
===================================================================
RCS file: /cvs/e/src/jsrc/org/quasiliteral/term/QuasiBuilder.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- QuasiBuilder.java	2001/12/19 22:36:13	1.3
+++ QuasiBuilder.java	2001/12/21 05:30:35	1.4
@@ -70,11 +70,12 @@
     AstroArg bag(AstroArg leftArg, AstroArg rightArg);
 
     /**
-     * Matches a consecutive sequence of Terms matched by optTerm.
+     * Matches a consecutive sequence of Terms matched by optTerm, where the
+     * number matched is constrained by quant.
      * <p>
      * Adds quant to the rank of optTerm.
      */
-    AstroArg seq(Astro optTerm, String quant);
+    AstroArg some(Astro optTerm, String quant);
 
     /**
      * Treat the 'args' list as a repeating sequence.



1.4       +1 -1      e/src/jsrc/org/quasiliteral/term/QuasiBuilderAdaptor.java

Index: QuasiBuilderAdaptor.java
===================================================================
RCS file: /cvs/e/src/jsrc/org/quasiliteral/term/QuasiBuilderAdaptor.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- QuasiBuilderAdaptor.java	2001/12/19 22:36:13	1.3
+++ QuasiBuilderAdaptor.java	2001/12/21 05:30:35	1.4
@@ -145,7 +145,7 @@
         throw new RuntimeException("not quasi-ing");
     }
 
-    public AstroArg seq(Astro optTerm, String quant) {
+    public AstroArg some(Astro optTerm, String quant) {
         throw new RuntimeException("not quasi-ing");
     }
 



1.20      +1 -1      e/src/jsrc/org/quasiliteral/term/Term.java

Index: Term.java
===================================================================
RCS file: /cvs/e/src/jsrc/org/quasiliteral/term/Term.java,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -r1.19 -r1.20
--- Term.java	2001/12/19 10:18:08	1.19
+++ Term.java	2001/12/21 05:30:35	1.20
@@ -300,7 +300,7 @@
     /**
      *
      */
-    public String asText() {
+    public String toString() {
         StringWriter strWriter = new StringWriter();
         try {
             prettyPrintOn(new TextWriter(strWriter), false);



1.17      +87 -11    e/src/jsrc/org/quasiliteral/term/Term.updoc

Index: Term.updoc
===================================================================
RCS file: /cvs/e/src/jsrc/org/quasiliteral/term/Term.updoc,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -r1.16 -r1.17
--- Term.updoc	2001/12/19 10:18:08	1.16
+++ Term.updoc	2001/12/21 05:30:35	1.17
@@ -232,35 +232,111 @@
     #                 x(3, 'x'),
     #                 zip)`
     
-    ? 
-    ? 
+
+
+
     ? def t1 := term`"foo"`
     # value: term`"foo"`
     
-    ? def t2 := term`bar(3, "x", zip(zap, 'x', 2.3))`
+    ? def tx := term`bar(3, "x", zip(zap, 'x', 2.3))`
     # value: term`bar(3,
     #                 "x",
     #                 zip(zap, 'x', 2.3))`
     
     ? def t2 := term`$t1(4)`
-    # problem: XXX not yet implemented
-    #
-    #   org.quasiliteral.quasiterm.QBuilder@12033fc valueMaker("${0}(4)")
-    #   <interactive interp> evalPrint(e`def t2 :any := term__quasiParser valueMaker("${0}(4)") substitute(ListMaker run(t1))`)
+    # value: term`"foo"(4)`
     
     ? def t3 := term`$t1(bar, $t2)`
     # value: term`"foo"(bar,
     #                   "foo"(4))`
     
-    ? def term`LiteralString(@x*)` := t3
-    # problem: XXX not yet implemented
+    ? def term`LiteralString@x` := t3
+    # value: term`"foo"(bar,
+    #                   "foo"(4))`
+    
+    ? x
+    # value: term`"foo"(bar,
+    #                   "foo"(4))`
+    
+    ? def term`LiteralInteger@f(@a1, @a2)` := t3
+    # problem: such-that expression was false
     #
-    #   ....(org.erights.e.develop.exception.NestedException: ...)
-    #   <interactive interp> evalPrint(e`def q_1 :any ? (term__quasiParser matchMaker("LiteralString(@{0}*)") matchBind(ListMaker run(), q_1) =~ [x :any]) := t3`)
+    #   <interactive interp> evalPrint(e`def q_3 :any ? (term__quasiParser matchMaker("LiteralInteger@{0}(@{1},...") matchBind(ListMaker run(), q_3) =~ [f :any, a1 :any, a2 :any]) := t3`)
+    
+    ? f
+    # value: <ref broken by problem: <NullPointerException: must be a EList rather than null>>
+    
+    ? def term`LiteralString@f(@a1, @a2)` := t3
+    # value: term`"foo"(bar,
+    #                   "foo"(4))`
+    
+    ? f
+    # value: term`"foo"`
+    
+    ? a1
+    # value: term`bar`
+    
+    ? a2
+    # value: term`"foo"(4)`
     
     ? 
+
+
+
+    ? def args := [term`foo`, term`bar(3)`]
+    # value: [term`foo`, term`bar(3)`]
+    
+    ? def t4 := term`zip($args*)`
+    # value: term`zip(foo,
+    #                 bar(3))`
+    
+    ? term`zip($args?)`
+    # problem: <AssertionError: Internal: Improper quantity: 2 vs ?>
+    #
+    #   term`zip(${0}?)` substitute([[term`foo`, term`bar(3)`]])
+    #   <interactive interp> evalPrint(e`term__quasiParser valueMaker("zip(${0}?)") substitute(ListMaker run(args))`)
+    
+    ? term`zip($args+)`
+    # value: term`zip(foo,
+    #                 bar(3))`
+    
+    ? term`zip(${[]}+)`
+    # problem: <AssertionError: Internal: Improper quantity: 0 vs +>
+    #
+    #   term`zip(${0}+)` substitute([[]])
+    #   <interactive interp> evalPrint(e`term__quasiParser valueMaker("zip(${0}+)") substitute(ListMaker run(ListMaker run()))`)
+    
+    ? term`zip(${[]}*)`
+    # value: term`zip`
+    
+    ? 
+
+    
+    ? 
+    ? def term`LiteralString(@args*)` := t3
+    # problem: XXX not yet implemented
+    #
+    #   org.quasiliteral.quasiterm.QBuilder@ff1fcedf matchMaker("LiteralString(@{0}*)")
+    #   <interactive interp> evalPrint(e`def q_5 :any ? (term__quasiParser matchMaker("LiteralString(@{0}*)") matchBind(ListMaker run(), q_5) =~ [args :any]) := t3`)
+    
     ? 
     ? 
+    ? def ta := term`foo(bar, bar(3), zip(zap))`
+    # value: term`foo(bar,
+    #                 bar(3),
+    #                 zip(zap))`
+    
+    ? def term`foo(bar@bars*, zip@z)` := ta
+    # value: term`foo(bar,
+    #                 bar(3),
+    #                 zip(zap))`
+    
+    ? bars
+    # value: [term`bar`, term`bar(3)`]
+    
+    ? z
+    # value: term`zip(zap)`
+    
     ? 
     ? 
     ? 



1.19      +33 -25    e/src/jsrc/org/quasiliteral/term/TermParser.java

Index: TermParser.java
===================================================================
RCS file: /cvs/e/src/jsrc/org/quasiliteral/term/TermParser.java,v
retrieving revision 1.18
retrieving revision 1.19
diff -u -r1.18 -r1.19
--- TermParser.java	2001/12/19 22:36:13	1.18
+++ TermParser.java	2001/12/21 05:30:35	1.19
@@ -314,7 +314,7 @@
 "functorHole : '@' '{' LiteralInteger '}'",
 };
 
-//#line 153 "term.y"
+//#line 152 "term.y"
 
 
 /**
@@ -398,6 +398,13 @@
 /**
  *
  */
+private void reserved(String s) throws SyntaxException {
+    yyerror("reserved: " + s);
+}
+
+/**
+ *
+ */
 private void yyerror(String s) throws SyntaxException {
     short tagCode = ((Astro)yylval).getOptTagCode();
     if (TermParser.EOFTOK == tagCode && "syntax error".equals(s)) {
@@ -469,6 +476,8 @@
     TheTokens['?']              = "Question";
     TheTokens['.']              = "Dot";
 
+    TheTokens['[']              = "OpenBracket";
+    TheTokens[']']              = "CloseBracket";
     TheTokens['(']              = "OpenParen";
     TheTokens[')']              = "CloseParen";
     TheTokens['{']              = "OpenBrace";
@@ -481,7 +490,7 @@
 static public final AstroSchema DEFAULT_SCHEMA =
   new BaseSchema("Term-Tree-Language", ConstList.fromArray(TheTokens));
 
-//#line 433 "TermParser.java"
+//#line 442 "TermParser.java"
 //###############################################################
 // method: yylexdebug : check lexer state
 //###############################################################
@@ -645,74 +654,73 @@
 break;
 case 7:
 //#line 89 "term.y"
-{ yyval = b.taggedHole((Astro)val_peek(1),
-                                                         (Astro)val_peek(0)); }
+{ yyval = b.taggedHole((Astro)val_peek(1), (Astro)val_peek(0)); }
 break;
 case 8:
-//#line 94 "term.y"
+//#line 93 "term.y"
 { yyval = b.list(); }
 break;
 case 10:
-//#line 99 "term.y"
+//#line 98 "term.y"
 { yyval = b.list((AstroArg)val_peek(0)); }
 break;
 case 11:
-//#line 100 "term.y"
+//#line 99 "term.y"
 { yyval = b.with(val_peek(2), (AstroArg)val_peek(0)); }
 break;
 case 13:
-//#line 105 "term.y"
+//#line 104 "term.y"
 { yyval = b.alt((AstroArg)val_peek(2), (AstroArg)val_peek(0));}
 break;
 case 15:
-//#line 110 "term.y"
+//#line 109 "term.y"
 { yyval = b.bag((AstroArg)val_peek(2), (AstroArg)val_peek(0));}
 break;
 case 17:
-//#line 118 "term.y"
-{ yyval = b.seq((Astro)val_peek(1), (String)val_peek(0)); }
+//#line 117 "term.y"
+{ yyval = b.some((Astro)val_peek(1), (String)val_peek(0)); }
 break;
 case 18:
-//#line 119 "term.y"
-{ yyval = b.seq(null,      (String)val_peek(0)); }
+//#line 118 "term.y"
+{ yyval = b.some(null,      (String)val_peek(0)); }
 break;
 case 19:
-//#line 120 "term.y"
-{ yyval = b.seq(null,      "."); }
+//#line 119 "term.y"
+{ reserved("Use '@{_}' for now"); }
 break;
 case 20:
-//#line 121 "term.y"
-{ yyval = b.group(val_peek(2),      (String)val_peek(0)); }
+//#line 120 "term.y"
+{ yyval = b.group(val_peek(2),       (String)val_peek(0)); }
 break;
 case 21:
-//#line 122 "term.y"
+//#line 121 "term.y"
 { yyval = b.unpack((Astro)val_peek(0)); }
 break;
 case 22:
-//#line 126 "term.y"
+//#line 125 "term.y"
 { yyval = "?"; }
 break;
 case 23:
-//#line 127 "term.y"
+//#line 126 "term.y"
 { yyval = "+"; }
 break;
 case 24:
-//#line 128 "term.y"
+//#line 127 "term.y"
 { yyval = "*"; }
 break;
 case 30:
-//#line 140 "term.y"
+//#line 139 "term.y"
 { yyval = untag((Astro)val_peek(0)); }
 break;
 case 31:
-//#line 147 "term.y"
+//#line 146 "term.y"
 { yyval = b.dollarHole((Astro)val_peek(1)); }
 break;
 case 32:
-//#line 148 "term.y"
+//#line 147 "term.y"
 { yyval = b.atHole(    (Astro)val_peek(1)); }
 break;
-//#line 661 "TermParser.java"
+//#line 669 "TermParser.java"
 //########## END OF USER-SUPPLIED ACTIONS ##########
     }//switch
     //#### Now let's reduce... ####



1.18      +31 -23    e/src/jsrc/org/quasiliteral/term/term.y

Index: term.y
===================================================================
RCS file: /cvs/e/src/jsrc/org/quasiliteral/term/term.y,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -r1.17 -r1.18
--- term.y	2001/12/19 22:36:13	1.17
+++ term.y	2001/12/21 05:30:35	1.18
@@ -74,58 +74,57 @@
  * </pre>
  */
 start:
-        term                         { myOptResult = b.start((Astro)$1); }
+        term                    { myOptResult = b.start((Astro)$1); }
 ;
 
 term:
-        functor                      { $$ = b.term((Astro)$1); }
- |      functor '(' argList ')'      { $$ = b.term((Astro)$1, $3); }
- |      '[' argList ']'              { $$ = b.tuple($2); }
+        functor                 { $$ = b.term((Astro)$1); }
+ |      functor '(' argList ')' { $$ = b.term((Astro)$1, $3); }
+ |      '[' argList ']'         { $$ = b.tuple($2); }
  ;
 
 functor:
         leaf
  |      functorHole
- |      id functorHole               { $$ = b.taggedHole((Astro)$1,
-                                                         (Astro)$2); }
+ |      id functorHole          { $$ = b.taggedHole((Astro)$1, (Astro)$2); }
  ;
 
 argList:
-        /* empty */                  { $$ = b.list(); }
+        /* empty */             { $$ = b.list(); }
  |      args
  ;
 
 args:
-        arg                          { $$ = b.list((AstroArg)$1); }
- |      args ',' arg                 { $$ = b.with($1, (AstroArg)$3); }
+        arg                     { $$ = b.list((AstroArg)$1); }
+ |      args ',' arg            { $$ = b.with($1, (AstroArg)$3); }
  ;
 
 arg:
         bag
- |      arg '|' bag                  { $$ = b.alt((AstroArg)$1, (AstroArg)$3);}
+ |      arg '|' bag             { $$ = b.alt((AstroArg)$1, (AstroArg)$3);}
  ;
 
 bag:
         seq
- |      bag '&' seq                  { $$ = b.bag((AstroArg)$1, (AstroArg)$3);}
+ |      bag '&' seq             { $$ = b.bag((AstroArg)$1, (AstroArg)$3);}
  ;
 
 /**
  * Each seq represents some number of Terms
  */
 seq:
-        term                         // An Astro is already a fine AstroArg
- |      term quant                   { $$ = b.seq((Astro)$1, (String)$2); }
- |           quant                   { $$ = b.seq(null,      (String)$1); }
- |           '.'                     { $$ = b.seq(null,      "."); }
- |      '(' args ')' quant           { $$ = b.group($2,      (String)$4); }
- |      '^' LiteralString            { $$ = b.unpack((Astro)$2); }
+        term                    // An Astro is already a fine AstroArg
+ |      term quant              { $$ = b.some((Astro)$1, (String)$2); }
+ |           quant              { $$ = b.some(null,      (String)$1); }
+ |           '.'                { reserved("Use '@{_}' for now"); }
+ |      '(' args ')' quant      { $$ = b.group($2,       (String)$4); }
+ |      '^' LiteralString       { $$ = b.unpack((Astro)$2); }
  ;
 
 quant:
-        '?'                          { $$ = "?"; }
- |      '+'                          { $$ = "+"; }
- |      '*'                          { $$ = "*"; }
+        '?'                     { $$ = "?"; }
+ |      '+'                     { $$ = "+"; }
+ |      '*'                     { $$ = "*"; }
  ;
 
 leaf:
@@ -137,15 +136,15 @@
  ;
 
 id:
-        ID                           { $$ = untag((Astro)$1); }
+        ID                      { $$ = untag((Astro)$1); }
  ;
 
 /**
  * Starts off as a hole for a Functor, but may get promoted.
  */
 functorHole:
-        '$' '{' LiteralInteger '}'   { $$ = b.dollarHole((Astro)$3); }
- |      '@' '{' LiteralInteger '}'   { $$ = b.atHole(    (Astro)$3); }
+        '$' '{' LiteralInteger '}'      { $$ = b.dollarHole((Astro)$3); }
+ |      '@' '{' LiteralInteger '}'      { $$ = b.atHole(    (Astro)$3); }
  ;
 
 
@@ -233,6 +232,13 @@
 /**
  *
  */
+private void reserved(String s) throws SyntaxException {
+    yyerror("reserved: " + s);
+}
+
+/**
+ *
+ */
 private void yyerror(String s) throws SyntaxException {
     short tagCode = ((Astro)yylval).getOptTagCode();
     if (TermParser.EOFTOK == tagCode && "syntax error".equals(s)) {
@@ -304,6 +310,8 @@
     TheTokens['?']              = "Question";
     TheTokens['.']              = "Dot";
 
+    TheTokens['[']              = "OpenBracket";
+    TheTokens[']']              = "CloseBracket";
     TheTokens['(']              = "OpenParen";
     TheTokens[')']              = "CloseParen";
     TheTokens['{']              = "OpenBrace";