source: branches/0.22.x/abcl/src/org/armedbear/lisp/Closure.java

Last change on this file was 12678, checked in by vvoutilainen, 15 years ago

Re #96: partial fix for argument lists where &key appears
before &rest. This fix takes care of the defun cases, but
some lambda cases still go unnoticed.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 36.8 KB
Line 
1/*
2 * Closure.java
3 *
4 * Copyright (C) 2002-2008 Peter Graves
5 * Copyright (C) 2008 Ville Voutilainen
6 * $Id: Closure.java 12678 2010-05-13 20:03:06Z vvoutilainen $
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 * As a special exception, the copyright holders of this library give you
23 * permission to link this library with independent modules to produce an
24 * executable, regardless of the license terms of these independent
25 * modules, and to copy and distribute the resulting executable under
26 * terms of your choice, provided that you also meet, for each linked
27 * independent module, the terms and conditions of the license of that
28 * module.  An independent module is a module which is not derived from
29 * or based on this library.  If you modify this library, you may extend
30 * this exception to your version of the library, but you are not
31 * obligated to do so.  If you do not wish to do so, delete this
32 * exception statement from your version.
33 */
34
35package org.armedbear.lisp;
36
37import static org.armedbear.lisp.Lisp.*;
38
39import java.util.ArrayList;
40
41public class Closure extends Function
42{
43  // Parameter types.
44  public static final int REQUIRED = 0;
45  public static final int OPTIONAL = 1;
46  public static final int KEYWORD  = 2;
47  public static final int REST     = 3;
48  public static final int AUX      = 4;
49
50  // States.
51  private static final int STATE_REQUIRED = 0;
52  private static final int STATE_OPTIONAL = 1;
53  private static final int STATE_KEYWORD  = 2;
54  private static final int STATE_REST     = 3;
55  private static final int STATE_AUX      = 4;
56
57  private Parameter[] requiredParameters = new Parameter[0];
58  private Parameter[] optionalParameters = requiredParameters;
59  private Parameter[] keywordParameters = requiredParameters;
60  private Parameter[] auxVars = requiredParameters;
61  private final LispObject body;
62  private final LispObject executionBody;
63  private final Environment environment;
64  private final boolean andKey;
65  private final boolean allowOtherKeys;
66  private Symbol restVar;
67  private Symbol envVar;
68  private int arity;
69
70  private int minArgs;
71  private int maxArgs;
72
73  private Symbol[] variables = new Symbol[0];
74  private LispObject specials = NIL;
75
76  private boolean bindInitForms;
77
78
79    /** Construct a closure object with a lambda-list described
80     * by these parameters.
81     *
82     *
83     * @param required Required parameters or an empty array for none
84     * @param optional Optional parameters or an empty array for none
85     * @param keyword Keyword parameters or an empty array for none
86     * @param keys NIL if the lambda-list doesn't contain &key, T otherwise
87     * @param rest the &rest parameter, or NIL if none
88     * @param moreKeys NIL if &allow-other-keys not present, T otherwise
89     */
90  public Closure(Parameter[] required,
91                 Parameter[] optional,
92                 Parameter[] keyword,
93                 Symbol keys, Symbol rest, Symbol moreKeys) {
94      minArgs = required.length;
95      maxArgs = (rest == NIL && moreKeys == NIL)
96          ? minArgs + optional.length + 2*keyword.length : -1;
97
98      arity = (rest == NIL && moreKeys == NIL && keys == NIL
99               && optional.length == 0)
100          ? maxArgs : -1;
101
102      requiredParameters = required;
103      optionalParameters = optional;
104      keywordParameters = keyword;
105
106      if (rest != NIL)
107        restVar = rest;
108
109      andKey = keys != NIL;
110      allowOtherKeys = moreKeys != NIL;
111      variables = processVariables();
112      bindInitForms = false;
113
114      // stuff we don't need: we're a compiled function
115      body = null;
116      executionBody = null;
117      environment = null;
118  }
119
120
121  public Closure(LispObject lambdaExpression, Environment env)
122  {
123    this(null, lambdaExpression, env);
124  }
125
126  public Closure(final LispObject name, final LispObject lambdaExpression,
127                 final Environment env)
128
129  {
130    super(name, lambdaExpression.cadr());
131    final LispObject lambdaList = lambdaExpression.cadr();
132    setLambdaList(lambdaList);
133    if (!(lambdaList == NIL || lambdaList instanceof Cons))
134      error(new ProgramError("The lambda list " + lambdaList.writeToString() +
135                           " is invalid."));
136    boolean _andKey = false;
137    boolean _allowOtherKeys = false;
138    if (lambdaList instanceof Cons)
139      {
140        final int length = lambdaList.length();
141        ArrayList<Parameter> required = null;
142        ArrayList<Parameter> optional = null;
143        ArrayList<Parameter> keywords = null;
144        ArrayList<Parameter> aux = null;
145        int state = STATE_REQUIRED;
146        LispObject remaining = lambdaList;
147        while (remaining != NIL)
148          {
149            LispObject obj = remaining.car();
150            if (obj instanceof Symbol)
151              {
152                if (state == STATE_AUX)
153                  {
154                    if (aux == null)
155                      aux = new ArrayList<Parameter>();
156                    aux.add(new Parameter((Symbol)obj, NIL, AUX));
157                  }
158                else if (obj == Symbol.AND_OPTIONAL)
159                  {
160                    state = STATE_OPTIONAL;
161                    arity = -1;
162                  }
163                else if (obj == Symbol.AND_REST || obj == Symbol.AND_BODY)
164                  {
165                    if (_andKey)
166                      {
167                        error(new ProgramError(
168                          "&REST/&BODY must precede &KEY."));
169                      }
170                    state = STATE_REST;
171                    arity = -1;
172                    maxArgs = -1;
173                    remaining = remaining.cdr();
174                    if (remaining == NIL)
175                      {
176                        error(new ProgramError(
177                          "&REST/&BODY must be followed by a variable."));
178                      }
179                    if (restVar != null) 
180                      {
181                        error(new ProgramError(
182                          "&REST/&BODY may occur only once."));
183                      }
184                    final LispObject remainingcar =  remaining.car();
185                    if (remainingcar instanceof Symbol)
186                      {
187                        restVar = (Symbol) remainingcar;
188                      }
189                    else
190                      {
191                        error(new ProgramError(
192                          "&REST/&BODY must be followed by a variable."));
193                      }
194                  }
195                else if (obj == Symbol.AND_ENVIRONMENT)
196                  {
197                    remaining = remaining.cdr();
198                    envVar = (Symbol) remaining.car();
199                    arity = -1; // FIXME
200                  }
201                else if (obj == Symbol.AND_KEY)
202                  {
203                    state = STATE_KEYWORD;
204                    _andKey = true;
205                    arity = -1;
206                  }
207                else if (obj == Symbol.AND_ALLOW_OTHER_KEYS)
208                  {
209                    _allowOtherKeys = true;
210                    maxArgs = -1;
211                  }
212                else if (obj == Symbol.AND_AUX)
213                  {
214                    // All remaining specifiers are aux variable specifiers.
215                    state = STATE_AUX;
216                    arity = -1; // FIXME
217                  }
218                else
219                  {
220                    if (state == STATE_OPTIONAL)
221                      {
222                        if (optional == null)
223                          optional = new ArrayList<Parameter>();
224                        optional.add(new Parameter((Symbol)obj, NIL, OPTIONAL));
225                        if (maxArgs >= 0)
226                          ++maxArgs;
227                      }
228                    else if (state == STATE_KEYWORD)
229                      {
230                        if (keywords == null)
231                          keywords = new ArrayList<Parameter>();
232                        keywords.add(new Parameter((Symbol)obj, NIL, KEYWORD));
233                        if (maxArgs >= 0)
234                          maxArgs += 2;
235                      }
236                    else
237                      {
238                        if (state != STATE_REQUIRED)
239                          {
240                            error(new ProgramError(
241                              "required parameters cannot appear after &REST/&BODY."));
242                          }
243                        if (required == null)
244                          required = new ArrayList<Parameter>();
245                        required.add(new Parameter((Symbol)obj));
246                        if (maxArgs >= 0)
247                          ++maxArgs;
248                      }
249                  }
250              }
251            else if (obj instanceof Cons)
252              {
253                if (state == STATE_AUX)
254                  {
255                    Symbol sym = checkSymbol(obj.car());
256                    LispObject initForm = obj.cadr();
257                    Debug.assertTrue(initForm != null);
258                    if (aux == null)
259                      aux = new ArrayList<Parameter>();
260                    aux.add(new Parameter(sym, initForm, AUX));
261                  }
262                else if (state == STATE_OPTIONAL)
263                  {
264                    Symbol sym = checkSymbol(obj.car());
265                    LispObject initForm = obj.cadr();
266                    LispObject svar = obj.cdr().cdr().car();
267                    if (optional == null)
268                      optional = new ArrayList<Parameter>();
269                    optional.add(new Parameter(sym, initForm, svar, OPTIONAL));
270                    if (maxArgs >= 0)
271                      ++maxArgs;
272                  }
273                else if (state == STATE_KEYWORD)
274                  {
275                    Symbol keyword;
276                    Symbol var;
277                    LispObject initForm = NIL;
278                    LispObject svar = NIL;
279                    LispObject first = obj.car();
280                    if (first instanceof Cons)
281                      {
282                        keyword = checkSymbol(first.car());
283                        var = checkSymbol(first.cadr());
284                      }
285                    else
286                      {
287                        var = checkSymbol(first);
288                        keyword =
289                          PACKAGE_KEYWORD.intern(var.name);
290                      }
291                    obj = obj.cdr();
292                    if (obj != NIL)
293                      {
294                        initForm = obj.car();
295                        obj = obj.cdr();
296                        if (obj != NIL)
297                          svar = obj.car();
298                      }
299                    if (keywords == null)
300                      keywords = new ArrayList<Parameter>();
301                    keywords.add(new Parameter(keyword, var, initForm, svar));
302                    if (maxArgs >= 0)
303                      maxArgs += 2;
304                  }
305                else
306                  invalidParameter(obj);
307              }
308            else
309              invalidParameter(obj);
310            remaining = remaining.cdr();
311          }
312        if (arity == 0)
313          arity = length;
314        if (required != null)
315          {
316            requiredParameters = new Parameter[required.size()];
317            required.toArray(requiredParameters);
318          }
319        if (optional != null)
320          {
321            optionalParameters = new Parameter[optional.size()];
322            optional.toArray(optionalParameters);
323          }
324        if (keywords != null)
325          {
326            keywordParameters = new Parameter[keywords.size()];
327            keywords.toArray(keywordParameters);
328          }
329        if (aux != null)
330          {
331            auxVars = new Parameter[aux.size()];
332            aux.toArray(auxVars);
333          }
334      }
335    else
336      {
337        // Lambda list is empty.
338        Debug.assertTrue(lambdaList == NIL);
339        arity = 0;
340        maxArgs = 0;
341      }
342    this.body = lambdaExpression.cddr();
343    LispObject bodyAndDecls = parseBody(this.body, false);
344    this.executionBody = bodyAndDecls.car();
345    this.specials = parseSpecials(bodyAndDecls.NTH(1));
346
347    this.environment = env;
348    this.andKey = _andKey;
349    this.allowOtherKeys = _allowOtherKeys;
350    minArgs = requiredParameters.length;
351    if (arity >= 0)
352      Debug.assertTrue(arity == minArgs);
353    variables = processVariables();
354  }
355
356  private final void processParameters(ArrayList<Symbol> vars,
357                                       final Parameter[] parameters)
358  {
359    for (Parameter parameter : parameters)
360      {
361        vars.add(parameter.var);
362        if (parameter.svar != NIL)
363          vars.add((Symbol)parameter.svar);
364        if (!bindInitForms)
365          if (!parameter.initForm.constantp())
366            bindInitForms = true;
367      }
368  }
369
370  // Also sets bindInitForms.
371  private final Symbol[] processVariables()
372  {
373    ArrayList<Symbol> vars = new ArrayList<Symbol>();
374    for (Parameter parameter : requiredParameters)
375      vars.add(parameter.var);
376    processParameters(vars, optionalParameters);
377    if (restVar != null)
378      {
379        vars.add(restVar);
380      }
381    processParameters(vars, keywordParameters);
382    Symbol[] array = new Symbol[vars.size()];
383    vars.toArray(array);
384    return array;
385  }
386
387  private static final void invalidParameter(LispObject obj)
388
389  {
390    error(new ProgramError(obj.writeToString() +
391                         " may not be used as a variable in a lambda list."));
392  }
393
394  @Override
395  public LispObject typep(LispObject typeSpecifier)
396  {
397    if (typeSpecifier == Symbol.COMPILED_FUNCTION)
398      return NIL;
399    return super.typep(typeSpecifier);
400  }
401
402  public final LispObject getVariableList()
403  {
404    LispObject result = NIL;
405    for (int i = variables.length; i-- > 0;)
406      result = new Cons(variables[i], result);
407    return result;
408  }
409
410  // Returns body as a list.
411  public final LispObject getBody()
412  {
413    return body;
414  }
415
416  public final Environment getEnvironment()
417  {
418    return environment;
419  }
420
421  @Override
422  public LispObject execute()
423  {
424    if (arity == 0)
425      {
426        return progn(executionBody, environment, 
427                     LispThread.currentThread());
428      }
429    else
430      return execute(new LispObject[0]);
431  }
432   
433  private final LispObject bindParametersAndExecute(LispObject... objects)
434
435  {
436    final LispThread thread = LispThread.currentThread();
437    final SpecialBindingsMark mark = thread.markSpecialBindings();
438    Environment ext = new Environment(environment);
439    bindRequiredParameters(ext, thread, objects);
440    if (arity != minArgs)
441      {
442        bindParameterDefaults(optionalParameters, ext, thread);
443        if (restVar != null)
444          bindArg(specials, restVar, NIL, ext, thread);
445        bindParameterDefaults(keywordParameters, ext, thread);
446      }
447    bindAuxVars(ext, thread);
448    declareFreeSpecials(ext);
449    try
450      {
451        return progn(executionBody, ext, thread);
452      }
453    finally
454      {
455        thread.resetSpecialBindings(mark);
456      }
457  }
458
459  private final void bindRequiredParameters(Environment ext,
460                                            LispThread thread,
461                                            LispObject[] objects)
462
463  {
464    // &whole and &environment before anything
465    if (envVar != null)
466      bindArg(specials, envVar, environment, ext, thread);
467    for (int i = 0; i < objects.length; ++i)
468      {
469        bindArg(specials, requiredParameters[i].var, objects[i], ext, thread);
470      }
471  }
472
473  public final LispObject invokeArrayExecute(LispObject... objects)
474
475  {
476    return execute(objects);
477  }
478
479  @Override
480  public LispObject execute(LispObject arg)
481  {
482    if (minArgs == 1)
483      {
484        return bindParametersAndExecute(arg);
485      }
486    else
487      {
488        return invokeArrayExecute(arg);
489      }
490  }
491
492  @Override
493  public LispObject execute(LispObject first, LispObject second)
494
495  {
496    if (minArgs == 2)
497      {
498        return bindParametersAndExecute(first, second);
499      }
500    else
501      {
502        return invokeArrayExecute(first, second);
503      }
504  }
505
506  @Override
507  public LispObject execute(LispObject first, LispObject second,
508                            LispObject third)
509
510  {
511    if (minArgs == 3)
512      {
513        return bindParametersAndExecute(first, second, third);
514      }
515    else
516      {
517        return invokeArrayExecute(first, second, third);
518      }
519  }
520
521  @Override
522  public LispObject execute(LispObject first, LispObject second,
523                            LispObject third, LispObject fourth)
524
525  {
526    if (minArgs == 4)
527      {
528        return bindParametersAndExecute(first, second, third, fourth);
529      }
530    else
531      {
532        return invokeArrayExecute(first, second, third, fourth);
533      }
534  }
535
536  @Override
537  public LispObject execute(LispObject first, LispObject second,
538                            LispObject third, LispObject fourth,
539                            LispObject fifth)
540
541  {
542    if (minArgs == 5)
543      {
544        return bindParametersAndExecute(first, second, third, fourth,
545                                        fifth);
546      }
547    else
548      {
549        return invokeArrayExecute(first, second, third, fourth, fifth);
550      }
551  }
552
553  @Override
554  public LispObject execute(LispObject first, LispObject second,
555                            LispObject third, LispObject fourth,
556                            LispObject fifth, LispObject sixth)
557
558  {
559    if (minArgs == 6)
560      {
561        return bindParametersAndExecute(first, second, third, fourth,
562                                        fifth, sixth);
563      }
564    else
565      {
566        return invokeArrayExecute(first, second, third, fourth, fifth,
567                                  sixth);
568      }
569  }
570
571  @Override
572  public LispObject execute(LispObject first, LispObject second,
573                            LispObject third, LispObject fourth,
574                            LispObject fifth, LispObject sixth,
575                            LispObject seventh)
576
577  {
578    if (minArgs == 7)
579      {
580        return bindParametersAndExecute(first, second, third, fourth,
581                               fifth, sixth, seventh);
582      }
583    else
584      {
585        return invokeArrayExecute(first, second, third, fourth, fifth,
586                                  sixth, seventh);
587      }
588  }
589
590  @Override
591  public LispObject execute(LispObject first, LispObject second,
592                            LispObject third, LispObject fourth,
593                            LispObject fifth, LispObject sixth,
594                            LispObject seventh, LispObject eighth)
595
596  {
597    if (minArgs == 8)
598      {
599        return bindParametersAndExecute(first, second, third, fourth,
600                               fifth, sixth, seventh, eighth);
601      }
602    else
603      {
604        return invokeArrayExecute(first, second, third, fourth, fifth,
605                                  sixth, seventh, eighth);
606      }
607  }
608
609  private final void declareFreeSpecials(Environment ext)
610
611  {
612    LispObject s = specials;
613    special:
614    while (s != NIL) {
615      Symbol special = (Symbol)s.car();
616      s = s.cdr();
617      for (Symbol var : variables)
618  if (special == var)
619          continue special;
620      for (Parameter parameter : auxVars)
621        if (special == parameter.var)
622          continue special;
623      ext.declareSpecial(special);
624    }
625  }
626
627  @Override
628  public LispObject execute(LispObject[] args)
629  {
630    final LispThread thread = LispThread.currentThread();
631    final SpecialBindingsMark mark = thread.markSpecialBindings();
632    Environment ext = new Environment(environment);
633    if (optionalParameters.length == 0 && keywordParameters.length == 0)
634      args = fastProcessArgs(args);
635    else
636      args = processArgs(args, thread);
637    Debug.assertTrue(args.length == variables.length);
638    if (envVar != null)
639      {
640        bindArg(specials, envVar, environment, ext, thread);
641      }
642    for (int i = 0; i < variables.length; i++)
643      {
644        Symbol sym = variables[i];
645        bindArg(specials, sym, args[i], ext, thread);
646      }
647    bindAuxVars(ext, thread);
648    declareFreeSpecials(ext);
649    try
650      {
651        return progn(executionBody, ext, thread);
652      }
653    finally
654      {
655        thread.resetSpecialBindings(mark);
656      }
657  }
658
659  protected final LispObject[] processArgs(LispObject[] args, LispThread thread)
660
661  {
662    if (optionalParameters.length == 0 && keywordParameters.length == 0)
663      return fastProcessArgs(args);
664    final int argsLength = args.length;
665    if (arity >= 0)
666      {
667        // Fixed arity.
668        if (argsLength != arity)
669          error(new WrongNumberOfArgumentsException(this));
670        return args;
671      }
672    // Not fixed arity.
673    if (argsLength < minArgs)
674      error(new WrongNumberOfArgumentsException(this));
675    final LispObject[] array = new LispObject[variables.length];
676    int index = 0;
677    // The bindings established here (if any) are lost when this function
678    // returns. They are used only in the evaluation of initforms for
679    // optional and keyword arguments.
680    final SpecialBindingsMark mark = thread.markSpecialBindings();
681    Environment ext = new Environment(environment);
682    // Section 3.4.4: "...the &environment parameter is bound along with
683    // &whole before any other variables in the lambda list..."
684    try {
685        if (bindInitForms)
686          if (envVar != null)
687            bindArg(specials, envVar, environment, ext, thread);
688        // Required parameters.
689        for (int i = 0; i < minArgs; i++)
690          {
691            if (bindInitForms)
692              bindArg(specials, requiredParameters[i].var, args[i], ext, thread);
693            array[index++] = args[i];
694          }
695        int i = minArgs;
696        int argsUsed = minArgs;
697        // Optional parameters.
698        for (Parameter parameter : optionalParameters)
699          {
700            if (i < argsLength)
701              {
702                if (bindInitForms)
703                  bindArg(specials, parameter.var, args[i], ext, thread);
704                array[index++] = args[i];
705                ++argsUsed;
706                if (parameter.svar != NIL)
707                  {
708                    if (bindInitForms)
709                      bindArg(specials, (Symbol)parameter.svar, T, ext, thread);
710                    array[index++] = T;
711                  }
712              }
713            else
714              {
715                // We've run out of arguments.
716                LispObject value;
717                if (parameter.initVal != null)
718                  value = parameter.initVal;
719                else
720                  value = eval(parameter.initForm, ext, thread);
721                if (bindInitForms)
722                  bindArg(specials, parameter.var, value, ext, thread);
723                array[index++] = value;
724                if (parameter.svar != NIL)
725                  {
726                    if (bindInitForms)
727                      bindArg(specials, (Symbol)parameter.svar, NIL, ext, thread);
728                    array[index++] = NIL;
729                  }
730              }
731            ++i;
732          }
733        // &rest parameter.
734        if (restVar != null)
735          {
736            LispObject rest = NIL;
737            for (int j = argsLength; j-- > argsUsed;)
738              rest = new Cons(args[j], rest);
739            if (bindInitForms)
740                bindArg(specials, restVar, rest, ext, thread);
741            array[index++] = rest;
742          }
743        // Keyword parameters.
744        if (keywordParameters.length > 0)
745          {
746            int argsLeft = argsLength - argsUsed;
747            if (argsLeft == 0)
748              {
749                // No keyword arguments were supplied.
750                // Bind all keyword parameters to their defaults.
751                for (int k = 0; k < keywordParameters.length; k++)
752                  {
753                    Parameter parameter = keywordParameters[k];
754                    LispObject value;
755                    if (parameter.initVal != null)
756                      value = parameter.initVal;
757                    else
758                      value = eval(parameter.initForm, ext, thread);
759                    if (bindInitForms)
760                        bindArg(specials, parameter.var, value, ext, thread);
761                    array[index++] = value;
762                    if (parameter.svar != NIL)
763                      {
764                        if (bindInitForms)
765                            bindArg(specials, (Symbol)parameter.svar, NIL, ext, thread);
766                        array[index++] = NIL;
767                      }
768                  }
769              }
770            else
771              {
772                if ((argsLeft % 2) != 0)
773                  error(new ProgramError("Odd number of keyword arguments."));
774                LispObject allowOtherKeysValue = null;
775                for (Parameter parameter : keywordParameters)
776                  {
777                    Symbol keyword = parameter.keyword;
778                    LispObject value = null;
779                    boolean unbound = true;
780                    for (int j = argsUsed; j < argsLength; j += 2)
781                      {
782                        if (args[j] == keyword)
783                          {
784                            if (bindInitForms)
785                                bindArg(specials, parameter.var, args[j+1], ext, thread);
786                            value = array[index++] = args[j+1];
787                            if (parameter.svar != NIL)
788                              {
789                                if (bindInitForms)
790                                    bindArg(specials,(Symbol)parameter.svar, T, ext, thread);
791                                array[index++] = T;
792                              }
793                            args[j] = null;
794                            args[j+1] = null;
795                            unbound = false;
796                            break;
797                          }
798                      }
799                    if (unbound)
800                      {
801                        if (parameter.initVal != null)
802                          value = parameter.initVal;
803                        else
804                          value = eval(parameter.initForm, ext, thread);
805                        if (bindInitForms)
806                            bindArg(specials, parameter.var, value, ext, thread);
807                        array[index++] = value;
808                        if (parameter.svar != NIL)
809                          {
810                            if (bindInitForms)
811                                bindArg(specials, (Symbol)parameter.svar, NIL, ext, thread);
812                            array[index++] = NIL;
813                          }
814                      }
815                    if (keyword == Keyword.ALLOW_OTHER_KEYS)
816                      {
817                        if (allowOtherKeysValue == null)
818                          allowOtherKeysValue = value;
819                      }
820                  }
821                if (!allowOtherKeys)
822                  {
823                    if (allowOtherKeysValue == null || allowOtherKeysValue == NIL)
824                      {
825                        LispObject unrecognizedKeyword = null;
826                        for (int j = argsUsed; j < argsLength; j += 2)
827                          {
828                            LispObject keyword = args[j];
829                            if (keyword == null)
830                              continue;
831                            if (keyword == Keyword.ALLOW_OTHER_KEYS)
832                              {
833                                if (allowOtherKeysValue == null)
834                                  {
835                                    allowOtherKeysValue = args[j+1];
836                                    if (allowOtherKeysValue != NIL)
837                                      break;
838                                  }
839                                continue;
840                              }
841                            // Unused keyword argument.
842                            boolean ok = false;
843                            for (Parameter parameter : keywordParameters)
844                              {
845                                if (parameter.keyword == keyword)
846                                  {
847                                    // Found it!
848                                    ok = true;
849                                    break;
850                                  }
851                              }
852                            if (ok)
853                              continue;
854                            // Unrecognized keyword argument.
855                            if (unrecognizedKeyword == null)
856                              unrecognizedKeyword = keyword;
857                          }
858                        if (unrecognizedKeyword != null)
859                          {
860                            if (!allowOtherKeys &&
861                                (allowOtherKeysValue == null || allowOtherKeysValue == NIL))
862                              error(new ProgramError("Unrecognized keyword argument " +
863                                                      unrecognizedKeyword.writeToString()));
864                          }
865                      }
866                  }
867              }
868          }
869        else if (argsUsed < argsLength)
870          {
871            // No keyword parameters.
872            if (argsUsed + 2 <= argsLength)
873              {
874                // Check for :ALLOW-OTHER-KEYS.
875                LispObject allowOtherKeysValue = NIL;
876                int n = argsUsed;
877                while (n < argsLength)
878                  {
879                    LispObject keyword = args[n];
880                    if (keyword == Keyword.ALLOW_OTHER_KEYS)
881                      {
882                        allowOtherKeysValue = args[n+1];
883                        break;
884                      }
885                    n += 2;
886                  }
887                if (allowOtherKeys || allowOtherKeysValue != NIL)
888                  {
889                    // Skip keyword/value pairs.
890                    while (argsUsed + 2 <= argsLength)
891                      argsUsed += 2;
892                  }
893                else if (andKey)
894                  {
895                    LispObject keyword = args[argsUsed];
896                    if (keyword == Keyword.ALLOW_OTHER_KEYS)
897                      {
898                        // Section 3.4.1.4: "Note that if &KEY is present, a
899                        // keyword argument of :ALLOW-OTHER-KEYS is always
900                        // permitted---regardless of whether the associated
901                        // value is true or false."
902                        argsUsed += 2;
903                      }
904                  }
905              }
906            if (argsUsed < argsLength)
907              {
908                if (restVar == null)
909                  error(new WrongNumberOfArgumentsException(this));
910              }
911          }
912    }
913    finally {
914        thread.resetSpecialBindings(mark);
915    }
916    return array;
917  }
918
919  // No optional or keyword parameters.
920  protected final LispObject[] fastProcessArgs(LispObject[] args)
921
922  {
923    final int argsLength = args.length;
924    if (arity >= 0)
925      {
926        // Fixed arity.
927        if (argsLength != arity)
928          error(new WrongNumberOfArgumentsException(this));
929        return args;
930      }
931    // Not fixed arity.
932    if (argsLength < minArgs)
933      error(new WrongNumberOfArgumentsException(this));
934    final LispObject[] array = new LispObject[variables.length];
935    int index = 0;
936    // Required parameters.
937    for (int i = 0; i < minArgs; i++)
938      {
939        array[index++] = args[i];
940      }
941    int argsUsed = minArgs;
942    // &rest parameter.
943    if (restVar != null)
944      {
945        LispObject rest = NIL;
946        for (int j = argsLength; j-- > argsUsed;)
947          rest = new Cons(args[j], rest);
948        array[index++] = rest;
949      }
950    else if (argsUsed < argsLength)
951      {
952        // No keyword parameters.
953        if (argsUsed + 2 <= argsLength)
954          {
955            // Check for :ALLOW-OTHER-KEYS.
956            LispObject allowOtherKeysValue = NIL;
957            int n = argsUsed;
958            while (n < argsLength)
959              {
960                LispObject keyword = args[n];
961                if (keyword == Keyword.ALLOW_OTHER_KEYS)
962                  {
963                    allowOtherKeysValue = args[n+1];
964                    break;
965                  }
966                n += 2;
967              }
968            if (allowOtherKeys || allowOtherKeysValue != NIL)
969              {
970                // Skip keyword/value pairs.
971                while (argsUsed + 2 <= argsLength)
972                  argsUsed += 2;
973              }
974            else if (andKey)
975              {
976                LispObject keyword = args[argsUsed];
977                if (keyword == Keyword.ALLOW_OTHER_KEYS)
978                  {
979                    // Section 3.4.1.4: "Note that if &key is present, a
980                    // keyword argument of :allow-other-keys is always
981                    // permitted---regardless of whether the associated
982                    // value is true or false."
983                    argsUsed += 2;
984                  }
985              }
986          }
987        if (argsUsed < argsLength)
988          {
989            if (restVar == null)
990              error(new WrongNumberOfArgumentsException(this));
991          }
992      }
993    return array;
994  }
995
996  private final void bindParameterDefaults(Parameter[] parameters,
997                                           Environment env,
998                                           LispThread thread)
999
1000  {
1001    for (Parameter parameter : parameters)
1002      {
1003        LispObject value;
1004        if (parameter.initVal != null)
1005          value = parameter.initVal;
1006        else
1007          value = eval(parameter.initForm, env, thread);
1008        bindArg(specials, parameter.var, value, env, thread);
1009        if (parameter.svar != NIL)
1010    bindArg(specials, (Symbol)parameter.svar, NIL, env, thread);
1011      }
1012  }
1013
1014  private final void bindAuxVars(Environment env, LispThread thread)
1015
1016  {
1017    // Aux variable processing is analogous to LET* processing.
1018    for (Parameter parameter : auxVars)
1019      {
1020        Symbol sym = parameter.var;
1021        LispObject value;
1022
1023        if (parameter.initVal != null)
1024          value = parameter.initVal;
1025        else
1026          value = eval(parameter.initForm, env, thread);
1027
1028        bindArg(specials, sym, value, env, thread);
1029      }
1030  }
1031
1032  public static class Parameter
1033  {
1034    final Symbol var;
1035    final LispObject initForm;
1036    final LispObject initVal;
1037    final LispObject svar;
1038    private final int type;
1039    final Symbol keyword;
1040
1041    public Parameter(Symbol var)
1042    {
1043      this.var = var;
1044      this.initForm = null;
1045      this.initVal = null;
1046      this.svar = NIL;
1047      this.type = REQUIRED;
1048      this.keyword = null;
1049    }
1050
1051    public Parameter(Symbol var, LispObject initForm, int type)
1052
1053    {
1054      this.var = var;
1055      this.initForm = initForm;
1056      this.initVal = processInitForm(initForm);
1057      this.svar = NIL;
1058      this.type = type;
1059      keyword =
1060        type == KEYWORD ? PACKAGE_KEYWORD.intern(var.name) : null;
1061    }
1062
1063    public Parameter(Symbol var, LispObject initForm, LispObject svar,
1064                     int type)
1065
1066    {
1067      this.var = var;
1068      this.initForm = initForm;
1069      this.initVal = processInitForm(initForm);
1070      this.svar = (svar != NIL) ? checkSymbol(svar) : NIL;
1071      this.type = type;
1072      keyword =
1073        type == KEYWORD ? PACKAGE_KEYWORD.intern(var.name) : null;
1074    }
1075
1076    public Parameter(Symbol keyword, Symbol var, LispObject initForm,
1077                     LispObject svar)
1078
1079    {
1080      this.var = var;
1081      this.initForm = initForm;
1082      this.initVal = processInitForm(initForm);
1083      this.svar = (svar != NIL) ? checkSymbol(svar) : NIL;
1084      type = KEYWORD;
1085      this.keyword = keyword;
1086    }
1087
1088    @Override
1089    public String toString()
1090    {
1091      if (type == REQUIRED)
1092        return var.toString();
1093      StringBuffer sb = new StringBuffer();
1094      if (keyword != null)
1095        {
1096          sb.append(keyword);
1097          sb.append(' ');
1098        }
1099      sb.append(var.toString());
1100      sb.append(' ');
1101      sb.append(initForm);
1102      sb.append(' ');
1103      sb.append(type);
1104      return sb.toString();
1105    }
1106
1107    private static final LispObject processInitForm(LispObject initForm)
1108
1109    {
1110      if (initForm.constantp())
1111        {
1112          if (initForm instanceof Symbol)
1113            return initForm.getSymbolValue();
1114          if (initForm instanceof Cons)
1115            {
1116              Debug.assertTrue(initForm.car() == Symbol.QUOTE);
1117              return initForm.cadr();
1118            }
1119          return initForm;
1120        }
1121      return null;
1122    }
1123  }
1124
1125  // ### lambda-list-names
1126  private static final Primitive LAMBDA_LIST_NAMES =
1127      new Primitive("lambda-list-names", PACKAGE_SYS, true)
1128    {
1129      @Override
1130      public LispObject execute(LispObject arg)
1131      {
1132        Closure closure = new Closure(list(Symbol.LAMBDA, arg, NIL), new Environment());
1133        return closure.getVariableList();
1134      }
1135    };
1136}
Note: See TracBrowser for help on using the repository browser.