internal Closure(Cons args, Env env, Interpreter interpreter, Loc loc)
        {
            this.interpreter = interpreter;
            this.loc = loc;
            ArgSpecs specs = analyzeArgSpec((Cons)args.first, env, loc);
            //create an env expanded by params in which to analyze body
            Env env2 = new Env(specs.parameters, null, env);

            this.argSpecs = specs;
            this.body = interpreter.analyze(new Cons(interpreter.BLOCK, args.rest), env2, loc);
            this.env = env;
        }
        //parse out params from spec (which may contain &optional, &key, &rest, initforms etc
        internal ArgSpecs analyzeArgSpec(Cons arglist, Env env, Loc loc)
        {
            //count the params
            int nParams = 0;
            Cons a = arglist;
            while (a != null)
            {
                Object p = a.first;
                if (p != interpreter.AMPOPT && p != interpreter.AMPKEY && p != interpreter.AMPREST)
                    ++nParams;
                a = a.rest;
            }

            ArgSpecs ret = new ArgSpecs(env);
            ret.parameters = new Parameter[nParams];
            Parameter.Spec state = Parameter.Spec.REQ;

            int param = 0;
            a = arglist;
            while (a != null)
            {
                Object p = a.first;
                switch (state)
                {
                    case Parameter.Spec.REQ:
                        if (p == interpreter.AMPOPT)
                            state = Parameter.Spec.OPT;
                        else if (p == interpreter.AMPKEY)
                            state = Parameter.Spec.KEY;
                        else if (p == interpreter.AMPREST)
                            state = Parameter.Spec.REST;
                        else
                        {
                            if (p is Symbol)
                            {
                                ret.parameters[param++] =
                                new Parameter((Symbol)p, Parameter.Spec.REQ, null);
                                ++ret.numReq;
                            }
                            else if (p is Cons)
                            {
                                ret.parameters[param] =
                                new Parameter((Symbol)((Cons)p).first, Parameter.Spec.REQ, null);
                                ret.parameters[param].typeSpec = interpreter.eval(Cons.Second((Cons)p), env);
                                ++param;
                                ++ret.numReq;
                            }
                        }
                        break;
                    case Parameter.Spec.OPT:
                        if (p == interpreter.AMPOPT)
                            throw new Exception("&optional can appear only once in arg list");
                        else if (p == interpreter.AMPKEY)
                            state = Parameter.Spec.KEY;
                        else if (p == interpreter.AMPREST)
                            state = Parameter.Spec.REST;
                        else
                        {
                            if (p is Symbol)
                            {
                                ret.parameters[param++] =
                                new Parameter((Symbol)p, Parameter.Spec.OPT, null);
                                ++ret.numOpt;
                            }
                            else if (p is Cons)
                            {
                                ret.parameters[param++] =
                                new Parameter((Symbol)((Cons)p).first, Parameter.Spec.OPT,
                                                  interpreter.analyze(Cons.Second((Cons)p), env, loc));
                                ++ret.numOpt;
                            }
                            else
                                throw new Exception("&optional parameters must be symbols or (symbol init-form)");
                        }
                        break;
                    case Parameter.Spec.KEY:
                        if (p == interpreter.AMPOPT)
                            throw new Exception("&optional must appear before &key in arg list");
                        else if (p == interpreter.AMPKEY)
                            throw new Exception("&key can appear only once in arg list");
                        else if (p == interpreter.AMPREST)
                            state = Parameter.Spec.REST;
                        else
                        {
                            if (p is Symbol)
                            {
                                ret.parameters[param] =
                                new Parameter((Symbol)p, Parameter.Spec.KEY, null);
                                ret.parameters[param].key = interpreter.intern(":" + ((Symbol)p).name);
                                ++param;
                                ++ret.numKey;
                            }
                            else if (p is Cons)
                            {
                                ret.parameters[param] =
                                new Parameter((Symbol)((Cons)p).first, Parameter.Spec.KEY,
                                                  interpreter.analyze(Cons.Second((Cons)p), env, loc));
                                ret.parameters[param].key =
                                interpreter.intern(":" + ((Symbol)((Cons)p).first).name);
                                ++param;
                                ++ret.numKey;
                            }
                            else
                                throw new Exception("&key parameters must be symbols or (symbol init-form)");
                        }
                        break;
                    case Parameter.Spec.REST:
                        if (p == interpreter.AMPOPT)
                            throw new Exception("&optional must appear before &rest in arg list");
                        else if (p == interpreter.AMPKEY)
                            throw new Exception("&key must appear before &rest in arg list");
                        else if (p == interpreter.AMPREST)
                            throw new Exception("&rest can appear only once in arg list");
                        else
                        {
                            if (!(p is Symbol))
                                throw new Exception("&rest parameter must be a symbol");
                            else
                            {
                                if (ret.numRest > 0) //already got a rest param
                                    throw new Exception("Only one &rest arg can be specified");
                                ret.parameters[param++] =
                                new Parameter((Symbol)p, Parameter.Spec.REST, null);
                                ++ret.numRest;
                            }
                        }
                        break;
                }

                a = a.rest;
            }

            return ret;
        }