[e-cvs] cvs commit: e/src/jsrc/org/quasiliteral/quasiterm DollarHole.java QuasiFunctor.java QuasiNode.java QuasiTerm.java QuasiTermBuilder.java

markm@eros.cs.jhu.edu markm@eros.cs.jhu.edu
Mon, 19 Nov 2001 14:33:02 -0500


markm       01/11/19 14:33:02

  Added:       src/jsrc/org/erights/e/elang/evm SimpleNounExpr.java
               src/jsrc/org/erights/e/elang/scope ContourBoundary.java
                        ScopeMapBase.java ScopeMapLink.java
               src/jsrc/org/erights/e/elib/base EComparable.java
               src/jsrc/org/erights/e/elib/tables Iteratable.java
               src/jsrc/org/quasiliteral/quasiterm DollarHole.java
                        QuasiFunctor.java QuasiNode.java QuasiTerm.java
                        QuasiTermBuilder.java
  Log:
  Oops, forgot these

Revision  Changes    Path
1.1                  e/src/jsrc/org/erights/e/elang/evm/SimpleNounExpr.java

Index: SimpleNounExpr.java
===================================================================
package org.erights.e.elang.evm;

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

    /**
     *
     */
    public SimpleNounExpr(String name) {
        super(name);
    }

    /**
     *
     */
    public boolean mayBeAssignable() {
        return true;
    }
}



1.1                  e/src/jsrc/org/erights/e/elang/scope/ContourBoundary.java

Index: ContourBoundary.java
===================================================================
package org.erights.e.elang.scope;

import org.erights.e.elang.evm.NounExpr;
import org.erights.e.elib.tables.FlexSet;
import org.erights.e.elib.tables.ConstSet;

/**
 * A ScopeMap consisting of an empty innermost contour on top of some other
 * ScopeMap.
 *
 * @author <a href="mailto:tribble@e-dean.com">E. Dean Tribble</a>
 * @author some mods by <a href="mailto:markm@erights.org">Mark S. Miller</a>
 */
/*package*/ class ContourBoundary extends ScopeMap {

    private ScopeMap myNext;

    /**
     *
     */
    /*package*/ ContourBoundary(ScopeMap outer) {
        myNext = outer;
    }

    /**
     *
     */
    public NounExpr getNoun(String name) {
        return myNext.getNoun(name);
    }

    /**
     *
     */
    public boolean contains(String name) {
        return myNext.contains(name);
    }

    /**
     *
     */
    public ConstSet namesSet() {
        return myNext.namesSet();
    }

    /**
     *
     */
    /*package*/ void addNamesTo(FlexSet names) {
        myNext.addNamesTo(names);
    }

    /**
     *
     */
    public void assertShadowable(String varName) {
        // Do nothing; we are at the end of the contour
    }

    /*
     * There is no need for two contiguous boundaries, so just return self.
     */
    public ScopeMap nested() {
        return this;
    }
}




1.1                  e/src/jsrc/org/erights/e/elang/scope/ScopeMapBase.java

Index: ScopeMapBase.java
===================================================================
package org.erights.e.elang.scope;

import org.erights.e.elib.tables.ConstMap;
import org.erights.e.elib.tables.ConstSet;
import org.erights.e.elib.tables.FlexSet;
import org.erights.e.elib.util.AlreadyDefinedException;
import org.erights.e.elang.evm.NounExpr;

/**
 * A single contour ScopeMap.
 *
 * @author <a href="mailto:tribble@e-dean.com">E. Dean Tribble</a>
 * @author some mods by <a href="mailto:markm@erights.org">Mark S. Miller</a>
 */
/*package*/ class ScopeMapBase extends ScopeMap {

    private final ConstMap myBindings;

    /**
     *
     */
    /*package*/ ScopeMapBase(ConstMap bindings) {
        myBindings = bindings;
    }

    /**
     *
     */
    public NounExpr getNoun(String name) {
        NounExpr optRes = (NounExpr)myBindings.get(name, null);
        if (optRes == null) {
            throw new UndefinedVariableException(name + " not in scope");
        }
        return optRes;
    }

    /**
     *
     */
    public boolean contains(String name) {
        return myBindings.maps(name);
    }

    /**
     *
     */
    public ConstSet namesSet() {
        return (ConstSet)myBindings.domain();
    }

    /**
     *
     */
    /*package*/ void addNamesTo(FlexSet names) {
        String[] varNames = (String[])myBindings.getKeys(String.class);
        for (int i = 0; i < varNames.length; i++) {
            names.addElement(varNames[i]);
        }
    }

    /**
     *
     */
    public void assertShadowable(String name) {
        if (contains(name)) {
            throw new AlreadyDefinedException(name + " already in scope");
        }
    }
}



1.1                  e/src/jsrc/org/erights/e/elang/scope/ScopeMapLink.java

Index: ScopeMapLink.java
===================================================================
package org.erights.e.elang.scope;

import org.erights.e.elang.evm.NounExpr;
import org.erights.e.elib.tables.ConstSet;
import org.erights.e.elib.tables.FlexSet;
import org.erights.e.elib.util.AlreadyDefinedException;

/**
 * A ScopeMap having at least one name -> NounExpr association in the
 * innermost contour.
 * <p>
 * Built from a single association and a previous ScopeMap.
 *
 * @author <a href="mailto:tribble@e-dean.com">E. Dean Tribble</a>
 * @author some mods by <a href="mailto:markm@erights.org">Mark S. Miller</a>
 */
/*package*/ class ScopeMapLink extends ScopeMap {

    private ScopeMap myNext;

    private final String myName;

    private final NounExpr myNoun;

    /**
     *
     */
    /*package*/ ScopeMapLink(String name, NounExpr noun, ScopeMap others) {
        myNext = others;
        myName = name;
        myNoun = noun;
    }

    /**
     *
     */
    public NounExpr getNoun(String name) {
        // TODO markm are we guaranteed that the names are canonical?
        return myName.equals(name) ? myNoun : myNext.getNoun(name);
    }

    /**
     *
     */
    public boolean contains(String name) {
        // TODO markm are we guaranteed that the names are canonical?
        return myName.equals(name) || myNext.contains(name);
    }

    /**
     *
     */
    /*package*/ void addNamesTo(FlexSet names) {
        myNext.addNamesTo(names);
        names.addElement(myName);
    }

    /**
     *
     */
    public void assertShadowable(String name) {
        // TODO markm are we guaranteed that the names are canonical?
        if (myName.equals(name)) {
            throw new AlreadyDefinedException(name + " already in scope");
        }
        myNext.assertShadowable(name);
    }
}




1.1                  e/src/jsrc/org/erights/e/elib/base/EComparable.java

Index: EComparable.java
===================================================================
package org.erights.e.elib.base;

/**
 * Like {@link Comparable}, but applies to irreflexive partial orders as well.
 *
 * @author <a href="mailto:markm@caplet.com">Mark S Miller</a>
 */
public interface EComparable {

    /**
     * 'x compareTo(y)' should return <ul>
     * <li>0.0 is x is equivalent (in the order) to y: 'x <=> y'.</li>
     * <li>A negative number if x is less than y: 'x < y'.</li>
     * <li>A positive number if x is greater than y: 'x > y'.</li>
     * <li>NaN is x and y are incomparable</li>
     * </ul>
     * This must follow the usual defintion of partial orders, except that we
     * don't require the order to be reflexive.  (In a reflexive order, for
     * all x, 'x <=> x'.)  Rather, we require that for all x, either x is <=>
     * itself or it is incomparable to itself.  This allows IEEE floating
     * point numbers to play, even though NaNs break conventional reflexivity
     * rules.
     */
    double compareTo(EComparable other);
}



1.1                  e/src/jsrc/org/erights/e/elib/tables/Iteratable.java

Index: Iteratable.java
===================================================================
package org.erights.e.elib.tables;

/**
 * The operation assumed by E's for-loop.
 * <p>
 * This should therefore be universal to all E collections.
 *
 * @author <a href="mailto:markm@caplet.com">Mark S Miller</a>
 */
public interface Iteratable {

    /**
     *
     */
    void iterate(AssocFunc func);
}



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

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

import antlr.Token;
import org.erights.e.develop.format.StringHelper;
import org.erights.e.elib.base.SourceSpan;
import org.erights.e.elib.base.EComparable;
import org.erights.e.elib.prim.E;
import org.erights.e.elib.prim.StaticMaker;
import org.erights.e.elib.serial.PassByConstruction;
import org.erights.e.elib.serial.Persistent;
import org.erights.e.elib.tables.ConstList;
import org.erights.e.elib.tables.ConstMap;
import org.erights.e.elib.tables.Selfless;
import org.erights.e.elib.tables.Twine;
import org.erights.e.elib.tables.FlexList;
import org.erights.e.elib.quasi.ValueMaker;
import org.erights.e.elib.quasi.MatchMaker;
import org.quasiliteral.astro.AstroToken;
import org.quasiliteral.term.TermLexer;
import org.quasiliteral.term.TermParser;
import org.quasiliteral.term.TermBuilder;
import org.quasiliteral.term.Functor;

import java.math.BigInteger;

/**
 * Corresponds to a "${<expression>}".
 * <p>
 * Depending on context, it may represent a Functor, a Term, or a list of
 * Terms.
 *
 * @author <a href="mailto:markm@caplet.com">Mark S Miller</a>
 */
public class DollarHole extends QuasiNode {

    /**
     *
     */
    public static StaticMaker DollarHoleMaker =
      StaticMaker.make(DollarHole.class);

    /**
     * @serial In normal usage, this should have the source position of the
     *         expression that was extracted to leave this hole behind.
     */
    private final Twine mySource;

    /**
     * @serial Which dollar-hole is this?
     */
    private final BigInteger myIndex;

    /**
     * @param index Which dollar-hole is this?
     */
    public DollarHole(Twine source, BigInteger index) {
        mySource = source;
        myIndex = index;
    }

    /**
     *
     */
    public Object qbuild(TermBuilder builder) {
        AstroToken token = new AstroToken(TermParser.LiteralInteger,
                                          mySource,
                                          myIndex);
        return builder.dollarHole(token);
    }

    /**
     * 'DollarHole new(mySource, myIndex)'
     */
    public Object[] getCanonicalState() {
        Object[] result = {
            DollarHoleMaker, "new",
            mySource, myIndex
        };
        return result;
    }

    /**
     * "Evaluates" to args[myIndex]
     */
    public Object substitute(Object[] args) {
        return args[myIndex.intValue()];
    }

    /**
     * Matches iff the specimen is <=> args[index], where this dollar-hole is
     * encoded as '${<index>}'.
     */
    public boolean matchBind(Object[] args,
                             Object specimen,
                             FlexList bindings)
    {
        if (! (specimen instanceof EComparable)) {
            return false;
        }
        EComparable mine = (EComparable)args[myIndex.intValue()];
        EComparable spec = (EComparable)specimen;
        //XXX BUG: If mine and spec are types that don't compare, this will
        //throw rather than returning false.  It's unclear where to fix this.
        return 0.0 == mine.compareTo(spec);
    }

    /**
     *
     */
    public String toString(boolean quasiFlag) {
        return "${" + myIndex + "}";
    }
}



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

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

import antlr.Token;
import org.erights.e.develop.format.StringHelper;
import org.erights.e.elib.base.SourceSpan;
import org.erights.e.elib.prim.E;
import org.erights.e.elib.prim.StaticMaker;
import org.erights.e.elib.serial.PassByConstruction;
import org.erights.e.elib.serial.Persistent;
import org.erights.e.elib.tables.ConstList;
import org.erights.e.elib.tables.ConstMap;
import org.erights.e.elib.tables.Selfless;
import org.erights.e.elib.tables.Twine;
import org.erights.e.elib.tables.FlexList;
import org.erights.e.elib.quasi.ValueMaker;
import org.erights.e.elib.quasi.MatchMaker;
import org.quasiliteral.astro.AstroToken;
import org.quasiliteral.term.TermLexer;
import org.quasiliteral.term.TermParser;
import org.quasiliteral.term.TermBuilder;
import org.quasiliteral.term.Functor;

import java.math.BigInteger;

/**
 * A quasiliteral form of a {@link Functor}, for generating or matching
 * actual Functors.
 *
 * @author <a href="mailto:markm@caplet.com">Mark S Miller</a>
 */
public class QuasiFunctor extends QuasiNode {

    /**
     *
     */
    public static StaticMaker QuasiFunctorMaker =
      StaticMaker.make(QuasiFunctor.class);

    /**
     * @serial The name of an Antlr token type int in a particular grammar.
     */
    private final String myName;

    /**
     * @serial The source text corresponding to the original source of the
     *         token.
     */
    private final Twine mySource;

    /**
     * Indicates the type of myValue.
     * <pre>
     *   If myValue is      myValueType is
     *       null              -1
     *       a Character       TermParser.LiteralChar
     *       a BigInteger      TermParser.LiteralInteger
     *       a Double          TermParser.LiteralFloat64
     *       a String          TermParser.LiteralString
     *       a Twine           TermParser.LiteralString
     * </pre>
     */
    private int myValueType;

    /**
     * @serial null, {@link Character}, {@link BigInteger}, {@link Double},
     *               {@link String}, or {@link Twine} calculated from lexing
     *               the token.  When matching, a null is treated as a don't
     *               care.
     */
    private Object myValue;

    /**
     * @param name The name of an Antlr token type int in a particular
     *             grammar.  Must be an identifier.  Printed only if not
     *             implied by value.  When matching, must match exactly.
     * @param source The source text corresponding to the original source of
     *               the token, although it isn't necessarily the same as the
     *               original source text.  In any case, its source span
     *               information says what positions in the original source
     *               it corresponds to. This is not part of the normal
     *               printed form.  Ignored when matching.
     * @param value null, {@link Character},
     *                 {@link BigInteger}, {@link Double},
     *                 {@link String}, or {@link Twine}
     *                 If non-null, is always part of the normal printed
     *                 form.  When matching, null means don't-care.
     */
    public QuasiFunctor(String name, Twine source, Object value) {
        if (!TermLexer.isIdentifier(name)) {
            throw new RuntimeException("Must be an identifier: " + name);
        }
        myName = name;
        mySource = source;
        if (null == value) {
            myValueType = -1;
        } else if (value instanceof Character) {
            myValueType = TermParser.LiteralChar;
        } else if (value instanceof Number) {
            if (value instanceof BigInteger) {
                myValueType = TermParser.LiteralInteger;
            } else if (value instanceof Double) {
                myValueType = TermParser.LiteralFloat64;
            } else {
                throw new RuntimeException
                  ("XXX Functor value coercion not yet implemented");
            }
        } else if (value instanceof String) {
            myValueType = TermParser.LiteralString;
            //XXX consider coercing value to Twine
        } else if (value instanceof Twine) {
            myValueType = TermParser.LiteralString;
        } else {
            throw new RuntimeException(value.getClass().getName() +
                                       " not a literal type");
        }
        myValue = value;
    }

    /**
     *
     */
    public QuasiFunctor(Functor functor) {
        this(functor.getName(), functor.getSource(), functor.getValue());
    }

    /**
     *
     */
    public Object qbuild(TermBuilder builder) {
        if (null == myValue) {
            return builder.functor(new AstroToken(TermParser.ID,
                                              mySource,
                                              myName));
        } else if (isJustLiteral()) {
            return builder.literal(new AstroToken(myValueType,
                                                  mySource,
                                                  myValue));
        } else {
            return builder.functor(new AstroToken(TermParser.ID,
                                                   Twine.fromString(""),
                                                   myName),
                                    new AstroToken(myValueType,
                                                   mySource,
                                                   myValue));
        }
    }

    /**
     * 'QuasiFunctorMaker new(myTokenType, mySource, myOptValue)'
     */
    public Object[] getCanonicalState() {
        Object[] result = {
            QuasiFunctorMaker, "new", myName, mySource, myValue
        };
        return result;
    }

    /**
     *
     */
    public String getName() {
        return myName;
    }

    /**
     *
     */
    public Twine getSource() {
        return mySource;
    }

    /**
     *
     */
    public Object getValue() {
        return myValue;
    }

    /**
     * Since a QuasiFunctor cannot yet contain any holes, this just returns
     * the equivalent Functor.
     */
    public Object substitute(Object[] args) {
        return new Functor(myName, mySource, myValue);
    }

    /**
     * If the specimen is a {@link Functor} that matches this QuasiFunctor,
     * then add to 'bindings' any aspects of specimen extracted by
     * corresponding at-holes, and return true.  Otherwise, return false.
     * <p>
     * Since QuasiFunctors currently contain no holes, this just determines
     * if the specimen matches.  For purposes of the match, the names must be
     * the same, the sources are ignored, and the values, if non-null, must be
     * <=> (the same magnitude).  This has the peculiar consequence that a
     * literal NaN can't be matched, since it isn't <=> to anything.  Further,
     * 0.0 will match -0.0, since they are <=>.
     * <p>
     * If this QuasiFunctor's myValue is null, this is treated as a don't
     * care.  We should eventually be able to use an at-hole for this purpose
     * as well.
     */
    public boolean matchBind(Object[] args,
                             Object specimen,
                             FlexList bindings)
    {
        if (! (specimen instanceof Functor)) {
            return false;
        }
        Functor spec = (Functor)specimen;
        if (! myName.equals(spec.getName())) {
            return false;
        }
        if (null == myValue) {
            return true;
        }
        if (myValueType != spec.getValueType()) {
            return false;
        }
        double comp =
          E.asFloat64(E.call(myValue, "compareTo", spec.getValue()));
        return 0.0 == comp;
    }

    /**
     * Is myName the name implied by the type of myValue?
     */
    private boolean isJustLiteral() {
        return TermParser.getTokenNames().get(myValueType).equals(myName);
    }

    /**
     *
     */
    public String toString(boolean quasiFlag) {
        if (null == myValue) {
            return myName;
        }
        String valueStr = E.toQuote(myValue).bare();
        if (quasiFlag) {
            valueStr = StringHelper.replaceAll(valueStr, "$", "$$");
            valueStr = StringHelper.replaceAll(valueStr, "@", "@@");
            valueStr = StringHelper.replaceAll(valueStr, "`", "``");
        }
        if (isJustLiteral()) {
            return valueStr;
        }
        return myName + ":" + valueStr;
    }
}



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

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

import antlr.Token;
import org.erights.e.develop.format.StringHelper;
import org.erights.e.elib.base.SourceSpan;
import org.erights.e.elib.prim.E;
import org.erights.e.elib.prim.StaticMaker;
import org.erights.e.elib.serial.PassByConstruction;
import org.erights.e.elib.serial.Persistent;
import org.erights.e.elib.tables.ConstList;
import org.erights.e.elib.tables.ConstMap;
import org.erights.e.elib.tables.Selfless;
import org.erights.e.elib.tables.Twine;
import org.erights.e.elib.tables.FlexList;
import org.erights.e.elib.quasi.ValueMaker;
import org.erights.e.elib.quasi.MatchMaker;
import org.quasiliteral.astro.AstroToken;
import org.quasiliteral.term.TermLexer;
import org.quasiliteral.term.TermParser;
import org.quasiliteral.term.TermBuilder;
import org.quasiliteral.term.Functor;

import java.math.BigInteger;

/**
 * A node in a quasiliteral Term/Functor tree.
 * <p>
 * The superclass of all the particular kinds of node in such a tree.
 *
 * @author <a href="mailto:markm@caplet.com">Mark S Miller</a>
 */
public abstract class QuasiNode
  implements Selfless, PassByConstruction, Persistent,
  ValueMaker, MatchMaker {

    /**
     * When built using the QuasiTermBuilder, the result should be
     * semantically equivalent to the original.
     */
    public abstract Object qbuild(TermBuilder builder);

    /**
     * quasiFlag defaults to false
     */
    public String toString() {
        return toString(false);
    }

    /**
     * If the printed form is read by the term__quasiParser (the
     * QuasiTermBuilder), the result should be semantically equivalent to the
     * original.
     */
    public abstract String toString(boolean quasiFlag);

    /**
     * matchBind/2 is implemented generically in terms of matchBind/3.
     */
    public ConstList matchBind(Object[] args, Object specimen) {
        FlexList bindings = FlexList.make();
        if (matchBind(args, specimen, bindings)) {
            return bindings.snapshot();
        } else {
            return null;
        }
    }
}



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

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

import org.erights.e.elib.tables.Selfless;
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.ConstMap;
import org.erights.e.elib.serial.PassByConstruction;
import org.erights.e.elib.serial.Persistent;
import org.erights.e.elib.eio.TextWriter;
import org.erights.e.elib.prim.StaticMaker;
import org.erights.e.elib.quasi.ValueMaker;
import org.erights.e.elib.quasi.MatchMaker;
import org.erights.e.develop.format.StringHelper;
import org.erights.e.develop.exception.ExceptionMgr;
import org.quasiliteral.astro.Astro;
import org.quasiliteral.term.Functor;
import org.quasiliteral.term.TermBuilder;
import org.quasiliteral.term.Term;
import antlr.collections.AST;
import antlr.Grammar;
import antlr.Token;
import antlr.CommonToken;
import antlr.CommonAST;

import java.io.IOException;
import java.io.StringWriter;

/**
 * A quasiliteral form of a {@link Term}, for generating or matching
 * actual Terms.
 *
 * @author <a href="mailto:markm@caplet.com">Mark S Miller</a>
 */
public class QuasiTerm extends QuasiNode {

    /**
     *
     */
    static public final StaticMaker QuasiTermMaker =
      StaticMaker.make(QuasiTerm.class);

    /** @serial Tags this QuasiTerm to explain its meaning. */
    private final QuasiNode myFunctor;

    /** @serial A list of QuasiTerms */
    private final ConstList myArgs;

    /**
     * Just used to decide how to pretty print.
     * <p>
     * Initialized lazily.  0 if uninitialized, so does not need to be
     * recalculated on revival.
     */
    private transient int myHeight = 0;

    /**
     * @param functor "explains" what kind of node this QuasiTerm is, and how
     *                to interpret the args.
     * @param args A list of the children QuasiTerms.
     */
    public QuasiTerm(QuasiNode functor, ConstList args) {
        myFunctor = functor;
        myArgs = args;
    }

    /**
     * Like a visitor pattern, where 'builder' is the visitor.
     * <p>
     * When built using the QuasiTermBuilder, the result should be
     * a semantically equivalent copy.
     */
    public Object qbuild(TermBuilder builder) {
        Object builtFunctor = myFunctor.qbuild(builder);
        Object builtArgs = builder.argList();
        int len = myArgs.size();
        for (int i = 0; i < len; i++) {
            QuasiTerm arg = (QuasiTerm)myArgs.get(i);
            builtArgs = builder.argList(builtArgs, arg.qbuild(builder));
        }
        return builder.term(builtFunctor, builtArgs);
    }

    /**
     *
     */
    public Object[] getCanonicalState() {
        Object[] result = { QuasiTermMaker, "new", myFunctor, myArgs };
        return result;
    }

    /**
     *
     */
    public QuasiNode getQuasiFunctor() {
        return myFunctor;
    }

    /**
     *
     */
    public ConstList getQuasiArgs() {
        return myArgs;
    }

    /**
     *"Evaluates" to a {@link Term}, by substituting the args for the
     * dollar-holes in this QuasiTerm / QuasiFunctor tree.
     */
    public Object substitute(Object[] args) {
        int len = myArgs.size();
        Object fobj = myFunctor.substitute(args);
        Functor functor = null;
        if (fobj instanceof Functor) {
            functor = (Functor)fobj;
        } else if (fobj instanceof Term) {
            Term fTerm = (Term)fobj;
            if (0 == len) {
                //XXX Bug: Grammar must distinguish term`$1`
                //vs term`$1()`.  Since it doesn't, we treat both as the first
                //and return $1 as the Term.
                return fTerm;
            } else if (0 == fTerm.getArgs().size()) {
                functor = fTerm.getFunctor();
            } else {
                throw new RuntimeException
                  ("XXX term where functor expected: " + fTerm);
            }
        }
        ConstList terms = ConstList.EmptyList;
        for (int i = 0; i < len; i++) {
            QuasiNode node = (QuasiNode)myArgs.get(i);
            Object val = node.substitute(args);
            if (val instanceof Term) {
                terms = terms.with((Term)val);
            } else {
                throw new RuntimeException("XXX not yet implemented");
            }
        }
        return new Term(functor, terms);
    }

    /**
     *
     */
    public boolean matchBind(Object[] args,
                             Object specimen,
                             FlexList bindings)
    {
        if (! (specimen instanceof Term)) {
            return false;
        }
        Term term = (Term)specimen;
        if (! myFunctor.matchBind(args, term.getFunctor(), bindings)) {
            return false;
        }
        int len = myArgs.size();
        ConstList terms = term.getArgs();
        for (int i = 0; i < len; i++) {
            Object thing = myArgs.get(i);
            if (thing instanceof QuasiTerm) {
                Term spec = (Term)terms.get(0);
                terms = terms.run(0, terms.size());
                if (! ((QuasiTerm)thing).matchBind(args, spec, bindings)) {
                    return false;
                }
            } else {
                throw new RuntimeException("XXX not yet implemented");
            }
        }
        return true;
    }

    /**
     * What's the longest distance to the bottom?
     * <p>
     * A leaf node is height 1.  All other nodes are one more than the height
     * of their highest child.  This is used for pretty printing.
     */
    public int getHeight() {
        if (myHeight <= 0) {
            myHeight = 1;
            for (int i = 0; i < myArgs.size(); i++) {
                int h = ((QuasiTerm)myArgs.get(i)).getHeight();
                myHeight = Math.max(myHeight, h + 1);
            }
        }
        return myHeight;
    }

    /**
     *
     */
    public String toString(boolean quasiFlag) {
        StringWriter strWriter = new StringWriter();
        try {
            prettyPrintOn(new TextWriter(strWriter), quasiFlag);
        } catch (Throwable th) {
            throw ExceptionMgr.asSafe(th);
        }
        return strWriter.getBuffer().toString();
    }

    /**
     *
     */
    public void printOn(TextWriter out) throws IOException {
        out.print("qterm`");
        prettyPrintOn(out.indent("      "), true);
        out.print("`");
    }

    /**
     *
     */
    public void prettyPrintOn(TextWriter out, boolean quasiFlag)
    throws IOException {
        String functorStr = myFunctor.toString(quasiFlag);
        out.print(functorStr);
        int h = getHeight();
        if (h <= 1) {
            if (myArgs.size() != 0) {
                throw new RuntimeException("internal: bad height " + h);
            }
            //If it's a leaf, don't show parens either
            return;
        }
        if (h == 2) {
            //If it only contains leaves, do it on one line
            out.print("(");
            ((QuasiTerm)myArgs.get(0)).prettyPrintOn(out, quasiFlag);
            for (int i = 1; i < myArgs.size(); i++) {
                out.print(", ");
                ((QuasiTerm)myArgs.get(i)).prettyPrintOn(out, quasiFlag);
            }
            out.print(")");
            return;
        }
        //print each child lined up.
        out.print("(");
        int reps = functorStr.length() + 1;
        String spaces = StringHelper.multiply(" ", reps);
        TextWriter sub = out.indent(spaces);

        ((QuasiTerm)myArgs.get(0)).prettyPrintOn(sub, quasiFlag);
        for (int i = 1; i < myArgs.size(); i++) {
            sub.println(",");
            ((QuasiTerm)myArgs.get(i)).prettyPrintOn(sub, quasiFlag);
        }
        sub.print(")");
    }
}



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

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

import org.erights.e.elib.tables.ConstList;
import org.erights.e.elib.tables.Twine;
import org.erights.e.elib.quasi.QuasiExprParser;
import org.erights.e.elib.quasi.QuasiPatternParser;
import org.erights.e.elib.quasi.ValueMaker;
import org.erights.e.elib.quasi.MatchMaker;
import org.quasiliteral.astro.AstroToken;
import org.quasiliteral.term.TermBuilder;
import org.quasiliteral.term.Term;
import org.quasiliteral.term.Functor;
import org.quasiliteral.term.TermParser;

import java.math.BigInteger;

/**
 * For building a quasiliteral for generating or matching Term/Functor trees.
 * <p>
 * The parameterization of types from TermBuilder are:<ul>
 * <li>PDollarHole -- ??</li>
 * <li>PAtHole -- ??</li>
 * <li>PHole -- ??</li>
 * <li>PDollarRepr -- ??</li>
 * <li>PFunctor -- {@link QuasiFunctor}</li>
 * <li>PTerm -- {@link QuasiTerm}</li>
 * <li>PTerms, PArgs -- a {@link ConstList} of {@link QuasiTerm}</li>
 * </ul>
 *
 * @author <a href="mailto:markm@caplet.com">Mark S Miller</a>
 */
public class QuasiTermBuilder
  implements TermBuilder, QuasiExprParser, QuasiPatternParser {

    /**
     *
     */
    static public final QuasiTermBuilder THE_ONE = new QuasiTermBuilder();

    /**
     *
     */
    private QuasiTermBuilder() {}

    /**
     *
     */
    public ValueMaker valueMaker(Twine template, int[] dlrHoles) {
        throw new RuntimeException
          ("XXX new quasi valueMaker API not yet implemented");
    }

    /**
     *
     */
    public ValueMaker valueMaker(Twine template) {
        return (QuasiTerm)TermParser.run(template, THE_ONE);
    }

    /**
     *
     */
    public MatchMaker matchMaker(Twine template,
                                 int[] dlrHoles,
                                 int[] atHoles)
    {
        throw new RuntimeException
          ("XXX new quasi matchMaker API not yet implemented");
    }

    /**
     *
     */
    public MatchMaker matchMaker(Twine template) {
        return (QuasiTerm)TermParser.run(template, THE_ONE);
    }

    /**
     * @return true
     */
    public boolean doesQuasis() { return true; }

    /**
     * @return :ConstList(QuasiTerm)
     */
    public Object argList() {
        return ConstList.EmptyList;
    }

    /**
     * @param first :QuasiTerm
     * @return :ConstList(QuasiTerm)
     */
    public Object argList(Object first) {
        return ConstList.EmptyList.with(first);
    }

    /**
     * @param list :ConstList(QuasiTerm)
     * @param next :QuasiTerm
     * @return :ConstList(QuasiTerm)
     */
    public Object argList(Object list, Object next) {
        return ((ConstList)list).with(next);
    }

    /**
     *
     */
    public Object term(Object fnctr) {
        //XXX Must promote hole to Term hole
        return term(fnctr, ConstList.EmptyList);
    }

    /**
     * @param fnctr :(QuasiNode
     * @param args :ConstList(QuasiNode)
     * @return :QuasiTerm
     */
    public Object term(Object fnctr, Object args) {
        return new QuasiTerm((QuasiNode)fnctr, (ConstList)args);
    }

    /**
     * @param ident :AstroToken(ID)
     * @return :QuasiFunctor
     */
    public Object functor(Object ident) {
        AstroToken tok = (AstroToken)ident;
        if (TermParser.ID != tok.getType()) {
            throw new RuntimeException("Must be ID: " + ident);
        }
        String name = (String)tok.getValue();
        if (null == name) {
            throw new RuntimeException("Must have value: " + ident);
        }
        return new QuasiFunctor(name, tok.getSource(), null);
    }

    /**
     * @param lit :AstroToken(Literal*)
     * @return :QuasiFunctor
     */
    public Object literal(Object lit) {
        AstroToken tok = (AstroToken)lit;
        //XXX check that it's a kind of literal
        Functor functor = tok.asFunctor(TermParser.getTokenNames());
        return new QuasiFunctor(functor);
    }

    /**
     * @param ident :AstroToken(ID)
     * @param lit :AstroToken(Literal*)
     * @return :QuasiFunctor
     */
    public Object functor(Object ident, Object lit) {
        AstroToken idTok = (AstroToken)ident;
        AstroToken litTok = (AstroToken)lit;

        if (TermParser.ID != idTok.getType()) {
            throw new RuntimeException("Must be ID: " + ident);
        }
        String name = (String)idTok.getValue();
        if (null == name) {
            throw new RuntimeException("Must have value: " + ident);
        }
        Twine source = (Twine)idTok.getSource()
          .add(":")
          .add(litTok.getSource());
        return new QuasiFunctor(name, source, litTok.getValue());
    }

    /**
     *
     */
    public Object dollarHole(Object index) {
        AstroToken token = ((AstroToken)index);
        return new DollarHole((Twine)token.getSource(),
                              (BigInteger)token.getValue());
    }

    /**
     *
     */
    public Object atHole(Object index) {
        throw new RuntimeException("XXX Not yet implemented");
    }

    /**
     *
     */
    public Object dollarQuant(Object dHole, Object quant) {
        throw new RuntimeException("XXX Not yet implemented");
    }

    /**
     *
     */
    public Object atQuant(Object optIdent, Object optQuant, Object atHole) {
        throw new RuntimeException("XXX Not yet implemented");
    }

    /**
     *
     */
    public String toString() { return "<QuasiTermBuilder>"; }
}