Ejemplo n.º 1
0
 private Symbol genVar() {
   return Symbol.gensym("VAR-" + (genVarCount++));
 }
Ejemplo n.º 2
0
/** This class represents a parameter spec of function. */
public final class LambdaList {

  private static final int REQUIRED = 1;
  private static final int OPTIONAL = 2;
  private static final int REST = 3;
  private static final int KEYWORD = 4;
  private static final int ALLOW_OTHER_KEYS = 5;
  private static final int AUX = 6;
  private static final int WHOLE = 7;
  private static final int BODY = 8;
  private static final int BEGIN = -1;
  private static final int END = -2;

  /** Uninterned symbol which represents the omitted svar. */
  public static final Symbol NOT_SPECIFIED = Symbol.gensym("_LL-VAR-NOT-SPECIFIED_");

  private Whole whole;
  private Required[] required;
  private Optional[] optional;
  private Rest rest;
  private Keyword[] keyword;
  private boolean allowOtherKeys;
  private Aux[] aux;

  private Object params = Symbols.NIL;
  private Object vars = null;
  private int genVarCount = 0;

  /**
   * LambdaList constructor for {@link Subr}.
   *
   * @param reqCount Number of required parameters
   * @param optCount Number of optional parameters
   * @param rest if true, then a function uses rest parameter
   * @param keys List of keywords for keyword parameters
   * @param allowOtherKeys if true, then a function accepts a keyword not defined in <code>keys
   *     </code>
   */
  public LambdaList(int reqCount, int optCount, boolean rest, Object keys, boolean allowOtherKeys) {
    int keyCount = Lists.length(keys);

    // whole: not supported in SUBR
    this.whole = null;
    // req
    this.required = new Required[reqCount];
    for (int i = 0; i < reqCount; i++) {
      this.required[i] = new Required(genVar());
    }
    // opt
    this.optional = new Optional[optCount];
    for (int i = 0; i < optCount; i++) {
      this.optional[i] = new Optional(genVar(), Symbols.NIL, genVar());
    }
    // rest
    if (rest) {
      this.rest = new Rest(genVar());
    } else {
      this.rest = null;
    }
    // key
    this.keyword = new Keyword[keyCount];
    this.allowOtherKeys = false;
    for (int i = 0; i < keyCount; i++) {
      Symbol key = Data.symbol(Lists.car(keys));
      // System.out.println("key = "+key);
      // if (key == Symbols.LK_ALLOW_OTHER_KEYS) {
      //    //System.out.println("allowOtherKeys = true");
      //    this.allowOtherKeys = true;
      //    keys = Lists.cdr(keys);
      //    if (!Lists.isEnd(keys)) {
      //        throw new ProgramException
      //            ("no variable is allowed right after "+
      //             "&allow-other-keys: ~S.",
      //             Lists.list(keys));
      //    }
      // }
      // else {
      //    this.keyword[i] = new Keyword
      //        (genVar(), key, Symbols.NIL, genVar());
      //    keys = Lists.cdr(keys);
      // }
      this.keyword[i] = new Keyword(genVar(), key, Symbols.NIL, genVar());
      keys = Lists.cdr(keys);
    }
    //
    this.allowOtherKeys = allowOtherKeys;
    // aux: not supported in SUBR
    this.aux = new Aux[0];
  }
  /**
   * LambdaList constructor for {@link Expr}.
   *
   * @param params List which represents a LambdaList
   * @param env
   */
  public LambdaList(Object params, Env env) {
    // if (Logger.tracelevelp(env))
    //    Logger.trace("[lambdaList] params=~S",
    //                 Lists.list(params), env);
    if (params == null) throw new NullPointerException("params is null");
    if (env == null) throw new NullPointerException("env is null");
    this.params = params;

    /*
     * parse params
     */
    Object obj = null;
    Object var = null;
    Object initform = null;
    Symbol svar = null;
    Symbol key = null;
    ArrayList tmp = new ArrayList();
    int wholeCount = 0;
    int reqCount = 0;
    int optCount = 0;
    int restCount = 0;
    int keyCount = 0;
    int allowOtherKeysCount = 0;
    int auxCount = 0;
    int step = BEGIN;
    while (true) {
      switch (step) {
        case BEGIN:
          if (params == Symbols.NIL) {
            step = END;
          } else if (Data.isAtom(params)) {
            step = REST;
            params = Lists.list(params);
          } else {
            obj = Lists.car(params);
            if (obj == Symbols.LK_WHOLE) {
              step = WHOLE;
              params = Lists.cdr(params);
            } else if (obj == Symbols.LK_OPTIONAL) {
              step = OPTIONAL;
              params = Lists.cdr(params);
            } else if (obj == Symbols.LK_REST || obj == Symbols.LK_BODY) {
              step = REST;
              params = Lists.cdr(params);
            } else if (obj == Symbols.LK_KEY) {
              step = KEYWORD;
              params = Lists.cdr(params);
            } else if (obj == Symbols.LK_AUX) {
              step = AUX;
              params = Lists.cdr(params);
            } else {
              step = REQUIRED;
            }
          }
          break;
        case WHOLE:
          /*
           * params pattern:
           * [&whole var]
           */
          if (Lists.isEnd(params)) {
            throw new ProgramException("whole parameter not exists", Symbols.NIL);
          }
          obj = Lists.car(params);
          var = Data.isPair(obj) ? new LambdaList(obj, env) : obj;
          tmp.add(new Whole(var));
          wholeCount++;
          params = Lists.cdr(params);
          if (params == Symbols.NIL) {
            step = END;
          } else if (Data.isAtom(params)) {
            step = REST;
            params = Lists.list(params);
          } else {
            obj = Lists.car(params);
            if (obj == Symbols.LK_OPTIONAL) {
              step = OPTIONAL;
              params = Lists.cdr(params);
            } else if (obj == Symbols.LK_REST || obj == Symbols.LK_BODY) {
              step = REST;
              params = Lists.cdr(params);
            } else if (obj == Symbols.LK_KEY) {
              step = KEYWORD;
              params = Lists.cdr(params);
            } else if (obj == Symbols.LK_AUX) {
              step = AUX;
              params = Lists.cdr(params);
            } else {
              step = REQUIRED;
            }
          }
          break;
        case REQUIRED:
          /*
           * params pattern:
           * {var}*
           */
          if (params == Symbols.NIL) {
            step = END;
          } else if (Data.isAtom(params)) {
            step = REST;
            params = Lists.list(params);
          } else {
            /* !Lists.isEnd(params) */
            obj = Lists.car(params);
            if (obj == Symbols.LK_OPTIONAL) {
              step = OPTIONAL;
              params = Lists.cdr(params);
            } else if (obj == Symbols.LK_REST || obj == Symbols.LK_BODY) {
              step = REST;
              params = Lists.cdr(params);
            } else if (obj == Symbols.LK_KEY) {
              step = KEYWORD;
              params = Lists.cdr(params);
            } else if (obj == Symbols.LK_AUX) {
              step = AUX;
              params = Lists.cdr(params);
            } else {
              var = Data.isPair(obj) ? new LambdaList(obj, env) : obj;
              tmp.add(new Required(var));
              reqCount++;

              step = REQUIRED;
              params = Lists.cdr(params);
            }
          }
          break;
        case OPTIONAL:
          /*
           * params pattern:
           * [&optional {var | (var [initform [svar]])}*]
           */
          if (params == Symbols.NIL) {
            step = END;
          } else if (Data.isAtom(params)) {
            step = REST;
            params = Lists.list(params);
          } else {
            /* !Lists.isEnd(params) */
            obj = Lists.car(params);
            if (obj == Symbols.LK_REST || obj == Symbols.LK_BODY) {
              step = REST;
              params = Lists.cdr(params);
            } else if (obj == Symbols.LK_KEY) {
              step = KEYWORD;
              params = Lists.cdr(params);
            } else if (obj == Symbols.LK_AUX) {
              step = AUX;
              params = Lists.cdr(params);
            } else {
              if (Data.isSymbol(obj)) {
                /*
                 * obj pattern:
                 * var <symbol>
                 */
                var = Data.symbol(obj);
                initform = Symbols.NIL;
                svar = NOT_SPECIFIED;
              } else {
                /*
                 * obj pattern:
                 * (var [initform [svar]])
                 */
                var =
                    Data.isPair(Lists.car(obj))
                        ? new LambdaList(Lists.car(obj), env)
                        : Lists.car(obj);
                initform = Lists.cadr(obj);
                svar = Lists.isEnd(Lists.cddr(obj)) ? NOT_SPECIFIED : Data.symbol(Lists.caddr(obj));
              }
              tmp.add(new Optional(var, initform, svar));
              optCount++;
              step = OPTIONAL;
              params = Lists.cdr(params);
            }
          }
          break;
        case REST:
          /*
           * params pattern:
           * [&rest var]
           */
          if (Lists.isEnd(params)) {
            throw new ProgramException("rest parameter not exists", Symbols.NIL);
          }
          obj = Lists.car(params);
          var = Data.isPair(obj) ? new LambdaList(obj, env) : obj;
          tmp.add(new Rest(var));
          restCount++;
          params = Lists.cdr(params);
          if (Lists.isEnd(params)) {
            step = END;
          } else {
            obj = Lists.car(params);
            if (obj == Symbols.LK_KEY) {
              step = KEYWORD;
              params = Lists.cdr(params);
            } else if (obj == Symbols.LK_AUX) {
              step = AUX;
              params = Lists.cdr(params);
            } else {
              throw new ProgramException(
                  "unacceptable variable: ~S" + " (expected: &key, &aux or empty)",
                  Lists.list(obj));
            }
          }
          break;
        case KEYWORD:
          /*
           * params pattern:
           * [&key {var | ({var | (keyword var)} [initform [svar]])}*]
           */
          if (Lists.isEnd(params)) {
            step = END;
          } else {
            obj = Lists.car(params);
            if (obj == Symbols.LK_ALLOW_OTHER_KEYS) {
              step = ALLOW_OTHER_KEYS;
              params = Lists.cdr(params);
            } else if (obj == Symbols.LK_AUX) {
              step = AUX;
              params = Lists.cdr(params);
            } else {
              if (Data.isSymbol(obj)) {
                /*
                 * obj pattern:
                 * var <symbol>
                 */
                var = Data.symbol(obj);
                initform = Symbols.NIL;
                svar = NOT_SPECIFIED;
                key = toKeyword(Data.symbol(var), env);
              } else {
                /*
                 * obj pattern:
                 * ({var | (keyword var)} [initform [svar]])
                 */
                var =
                    Data.isSymbol(Lists.car(obj))
                        ? Lists.car(obj)
                        : Data.isSymbol(Lists.cadar(obj))
                            ? Lists.cadar(obj)
                            : new LambdaList(Lists.cadar(obj), env);
                initform = Lists.cadr(obj);
                svar = Lists.isEnd(Lists.cddr(obj)) ? NOT_SPECIFIED : Data.symbol(Lists.caddr(obj));
                key =
                    Data.isSymbol(Lists.car(obj))
                        ? toKeyword(Data.symbol(var), env)
                        : Data.symbol(Lists.caar(obj));
              }
              tmp.add(new Keyword(var, key, initform, svar));
              keyCount++;
              step = KEYWORD;
              params = Lists.cdr(params);
            }
          }
          break;
        case ALLOW_OTHER_KEYS:
          // System.out.println("in ALLOW_OTHER_KEYS");
          allowOtherKeysCount++;
          if (Lists.isEnd(params)) {
            // System.out.println("-> END");
            step = END;
          } else {
            obj = Lists.car(params);
            // System.out.println("-> obj="+obj);
            if (obj == Symbols.LK_AUX) {
              step = AUX;
              params = Lists.cdr(params);
            } else {
              throw new ProgramException(
                  "unacceptable variable: ~S" + " (expected: &aux or empty)", Lists.list(obj));
            }
          }
          break;
        case AUX:
          /*
           * params pattern:
           * [&aux {var | (var [initform])}*]
           */
          if (Lists.isEnd(params)) {
            step = END;
          } else {
            obj = Lists.car(params);
            if (Data.isSymbol(obj)) {
              /*
               * obj pattern:
               * var <symbol>
               */
              var = Data.symbol(obj);
              initform = Symbols.NIL;
            } else {
              /*
               * obj pattern:
               * (var [initform])
               */
              var =
                  Data.isSymbol(Lists.car(obj))
                      ? Lists.car(obj)
                      : new LambdaList(Lists.car(obj), env);
              initform = Lists.cadr(obj);
            }
            tmp.add(new Aux(var, initform));
            auxCount++;
            step = AUX;
            params = Lists.cdr(params);
          }
          break;
        case END:
          Iterator it = tmp.iterator();
          this.required = new Required[reqCount];
          this.optional = new Optional[optCount];
          this.keyword = new Keyword[keyCount];
          this.aux = new Aux[auxCount];

          // fill whole param
          this.whole = (wholeCount > 0) ? (Whole) it.next() : null;
          // fill required params
          for (int i = 0; i < reqCount; i++) this.required[i] = (Required) it.next();
          // fill optional params
          for (int i = 0; i < optCount; i++) this.optional[i] = (Optional) it.next();
          // fill rest param
          this.rest = (restCount > 0) ? (Rest) it.next() : null;
          // fill keyword params
          for (int i = 0; i < keyCount; i++) this.keyword[i] = (Keyword) it.next();
          // fill allowOtherKeys flag
          this.allowOtherKeys = (allowOtherKeysCount > 0);
          // fill aux params
          for (int i = 0; i < auxCount; i++) this.aux[i] = (Aux) it.next();
          return;
        default:
          throw new NotReachedException("unacceptable step: " + step, Symbols.NIL);
      }
    }
  }

  private Symbol genVar() {
    return Symbol.gensym("VAR-" + (genVarCount++));
  }

  private Symbol toKeyword(Symbol var, Env env) {
    return Data.symbol(env.lisp().getObarray().intern(Package.KEYWORD, var.pname()).nth(0));
  }

  /**
   * Returns the parameter spec list passed to the constructor of this class for the EXPR. If this
   * object is constructed via the constructor for the SUBR, NIL is returned.
   */
  public Object params() {
    return params;
  }
  /** Returns a flat list of variables held by this LambdaList. */
  public Object vars() {
    if (this.vars != null) return this.vars;

    Object ret = Symbols.NIL;
    // whole
    if (this.whole != null) ret = Lists.nconc(this.whole.vars(), ret);
    // required
    for (int i = 0; i < this.required.length; i++) ret = Lists.nconc(this.required[i].vars(), ret);
    // optional
    for (int i = 0; i < this.optional.length; i++) ret = Lists.nconc(this.optional[i].vars(), ret);
    // rest
    if (this.rest != null) ret = Lists.nconc(this.rest.vars(), ret);
    // keyword
    for (int i = 0; i < this.keyword.length; i++) ret = Lists.nconc(this.keyword[i].vars(), ret);
    // aux
    for (int i = 0; i < this.aux.length; i++) ret = Lists.nconc(this.aux[i].vars(), ret);

    this.vars = ret;

    return ret;
  }
  /** Returns true if this LambdaList is nested. */
  public boolean isNested() {
    // check whole param
    if (this.whole != null) if (!Data.isSymbol(this.whole.var)) return true;
    // check required params
    for (int i = 0; i < this.required.length; i++)
      if (!Data.isSymbol(this.required[i].var)) return true;
    // check optional params
    for (int i = 0; i < this.optional.length; i++)
      if (!Data.isSymbol(this.optional[i].var)) return true;
    // check rest param
    if (this.rest != null) if (!Data.isSymbol(this.rest.var)) return true;
    // check keyword params
    for (int i = 0; i < this.keyword.length; i++)
      if (!Data.isSymbol(this.keyword[i].var)) return true;
    // check aux params
    for (int i = 0; i < this.aux.length; i++) if (!Data.isSymbol(this.aux[i].var)) return true;
    return false;
  }
  /**
   * Returns a variable for either required or optional parameter at the specified index.
   *
   * @param i index, which must satisfy <code>
   *        0 &lt;= i &amp;&amp;
   *        i &lt; {@link #reqCount reqCount} + {@link #optCount optCount}
   *        </code>
   * @return Symbol which represents the variable, or sub LambdaList if this LambdaList is nested
   */
  public Object var(int i) {
    if (i < reqCount()) return reqVar(i);
    else return optVar(i - reqCount());
  }
  /** Returns true if the whole parameter exists. */
  public boolean isWhole() {
    return whole != null;
  }
  /**
   * Returns a variable for the whole parameter.
   *
   * @return Symbol which represents the variable
   */
  public Object wholeVar() {
    return whole.var;
  }
  /** Returns the number of the required parameters. */
  public int reqCount() {
    return required.length;
  }
  /**
   * Returns a variable for the required parameter at the specified index.
   *
   * @param i index, which must satisfy <code>
   *        0 &lt;= i &amp;&amp;
   *        i &lt; {@link #reqCount reqCount}
   *        </code>
   * @return Symbol which represents the variable, or sub LambdaList if this LambdaList is nested
   */
  public Object reqVar(int i) {
    return required[i].var;
  }
  /** Returns the number of the optional parameters. */
  public int optCount() {
    return optional.length;
  }
  /**
   * Returns a variable for the optional parameter at the specified index.
   *
   * @param i index, which must satisfy <code>
   *        0 &lt;= i &amp;&amp;
   *        i &lt; {@link #optCount optCount}
   *        </code>
   * @return Symbol which represents the variable, or sub LambdaList if this LambdaList is nested
   */
  public Object optVar(int i) {
    return optional[i].var;
  }
  /**
   * Returns a initform for the optional parameter at the specified index.
   *
   * @param i index, which must satisfy <code>
   *        0 &lt;= i &amp;&amp;
   *        i &lt; {@link #optCount optCount}
   *        </code>
   * @return Initform
   */
  public Object optInitform(int i) {
    return optional[i].initform;
  }
  /**
   * Returns a suppliedp variable for the optional parameter at the specified index. When the
   * suppliedp variable is not defined in this LambdaList, {@link #NOT_SPECIFIED} is returned.
   *
   * @param i index, which must satisfy <code>
   *        0 &lt;= i &amp;&amp;
   *        i &lt; {@link #optCount optCount}
   *        </code>
   * @return Symbol which represents the variable
   */
  public Symbol optSvar(int i) {
    return optional[i].svar;
  }
  /** Returns true if the rest parameter exists. */
  public boolean isRest() {
    return rest != null;
  }
  /**
   * Returns a variable for the rest parameter.
   *
   * @return Symbol which represents the variable, or sub LambdaList if this LambdaList is nested
   */
  public Object restVar() {
    return rest.var;
  }
  /** Returns the number of the keyword parameters. */
  public int keyCount() {
    return keyword.length;
  }
  /** Returns true if keywords not included in the definition of this LambdaList are allowed. */
  public boolean allowOtherKeys() {
    return allowOtherKeys;
  }
  /**
   * Returns a variable for the keyword parameter at the specified index.
   *
   * @param i index, which must satisfy <code>
   *        0 &lt;= i &amp;&amp;
   *        i &lt; {@link #keyCount keyCount}
   *        </code>
   * @return Symbol which represents the variable, or sub LambdaList if this LambdaList is nested
   */
  public Object keyVar(int i) {
    return keyword[i].var;
  }
  /**
   * Returns a keyword for the keyword parameter at the specified index.
   *
   * @param i index, which must satisfy <code>
   *        0 &lt;= i &amp;&amp;
   *        i &lt; {@link #keyCount keyCount}
   *        </code>
   * @return Symbol which represents the keyword
   */
  public Symbol keyKey(int i) {
    return keyword[i].key;
  }
  /**
   * Returns a initform for the keyword parameter at the specified index.
   *
   * @param i index, which must satisfy <code>
   *        0 &lt;= i &amp;&amp;
   *        i &lt; {@link #keyCount keyCount}
   *        </code>
   * @return Initform
   */
  public Object keyInitform(int i) {
    return keyword[i].initform;
  }
  /**
   * Returns a suppliedp variable for the keyword parameter at the specified index. When the
   * suppliedp variable is not defined in this LambdaList, {@link #NOT_SPECIFIED} is returned.
   *
   * @param i index, which must satisfy <code>
   *        0 &lt;= i &amp;&amp;
   *        i &lt; {@link #keyCount keyCount}
   *        </code>
   * @return Symbol which represents the variable
   */
  public Symbol keySvar(int i) {
    return keyword[i].svar;
  }
  /** Returns the number of the aux parameters. */
  public int auxCount() {
    return aux.length;
  }
  /**
   * Returns a variable for the aux parameter at the specified index.
   *
   * @param i index, which must satisfy <code>
   *        0 &lt;= i &amp;&amp;
   *        i &lt; {@link #auxCount auxCount}
   *        </code>
   * @return Symbol which represents the variable, or sub LambdaList if this LambdaList is nested
   */
  public Object auxVar(int i) {
    return aux[i].var;
  }
  /**
   * Returns a initform for the aux parameter at the specified index.
   *
   * @param i index, which must satisfy <code>
   *        0 &lt;= i &amp;&amp;
   *        i &lt; {@link #auxCount auxCount}
   *        </code>
   * @return Initform
   */
  public Object auxInitform(int i) {
    return aux[i].initform;
  }

  /*
   * nested classes
   */

  abstract static class Param {
    final Object var;

    Param(Object var) {
      this.var = checkVar(var);
    }

    Object checkVar(Object var) {
      if (var == null) {
        throw new NullPointerException("var is null");
      } else if (Data.isSymbol(var)) {
        Symbol sym = Data.symbol(var);
        if (sym.isSelfeval() || Symbols.isLambdaListKeyword(sym))
          throw new ProgramException("illegal symbol specified for var: ~S.", Lists.list(var));
      } else {
        if (!Data.isLambdaList(var))
          throw new ProgramException("illegal object specified for var: ~S.", Lists.list(var));
      }
      return var;
    }

    Object checkInitform(Object initform) {
      if (initform == null) throw new NullPointerException("initform is null");
      return initform;
    }

    Symbol checkSvar(Symbol svar) {
      if (svar == null) throw new NullPointerException("svar is null");
      else if (svar.isSelfeval())
        throw new ProgramException("illegal symbol specified for svar: ~S.", Lists.list(svar));
      return svar;
    }

    Symbol checkKey(Symbol key) {
      if (key == null) throw new NullPointerException("key is null");
      else if (!key.isSelfeval() || key == Symbols.NIL || key == Symbols.T)
        throw new ProgramException("illegal symbol specified for key: ~S.", Lists.list(key));
      return key;
    }

    Object vars() {
      if (Data.isSymbol(var)) return Lists.list(var);
      else return Data.lambdaList(var).vars();
    }
  } /* end of Param */

  static class Required extends Param {
    Required(Object var) {
      super(var);
    }
  } /* end of Required */

  static class Optional extends Param {
    final Object initform;
    final Symbol svar;

    Optional(Object var, Object initform, Symbol svar) {
      super(var);
      this.initform = checkInitform(initform);
      this.svar = checkSvar(svar);
    }

    Object vars() {
      if (svar == NOT_SPECIFIED) return super.vars();
      else if (Data.isSymbol(var)) return Lists.list(svar, var);
      else return Lists.cons(svar, Data.lambdaList(var).vars());
    }
  } /* end of Optional */

  static class Rest extends Param {
    Rest(Object var) {
      super(var);
    }
  } /* end of Rest */

  static class Keyword extends Param {
    final Symbol key;
    final Object initform;
    final Symbol svar;

    Keyword(Object var, Symbol key, Object initform, Symbol svar) {
      super(var);
      this.key = checkKey(key);
      this.initform = checkInitform(initform);
      this.svar = checkSvar(svar);
    }

    Object vars() {
      if (svar == NOT_SPECIFIED) return super.vars();
      else if (Data.isSymbol(var)) return Lists.list(svar, var);
      else return Lists.cons(svar, Data.lambdaList(var).vars());
    }
  } /* end of Keyword */

  static class Aux extends Param {
    final Object initform;

    Aux(Object var, Object initform) {
      super(var);
      this.initform = checkInitform(initform);
    }
  } /* end of Aux */

  static class Whole extends Param {
    Whole(Object var) {
      super(var);
    }
  } /* end of Whole */
} /* end of LambdaList */