//......................................................................... public Closure(Cons args, IEnvironment env, Interpreter interpreter, Location loc) { InnerInterpreter = interpreter; InnerLocation = loc; ArgSpecs specs = AnalyzeArgSpec((Cons)args.First, env, loc); //create an env expanded by params in which to analyze body IEnvironment env2 = new Environment(specs.Parameters, null, env); InnerArgsSpecs = specs; InnerBody = interpreter.Analyze(new Cons(interpreter.BLOCK, args.Rest), env2, loc); InnerEnvironment = env; }
//parse out params from spec (which may contain &optional, &key, &rest, initforms etc protected ArgSpecs AnalyzeArgSpec(Cons arglist, IEnvironment env, Location loc) { //count the params int nParams = 0; Cons a = arglist; while (a != null) { Object p = a.First; if (p != InnerInterpreter.AMPOPT && p != InnerInterpreter.AMPKEY && p != InnerInterpreter.AMPREST) ++nParams; a = a.Rest; } ArgSpecs ret = new ArgSpecs(env); ret.Parameters = new Parameter[nParams]; Parameter.Specification state = Parameter.Specification.REQ; int param = 0; a = arglist; while (a != null) { Object p = a.First; switch (state) { case Parameter.Specification.REQ: if (p == InnerInterpreter.AMPOPT) state = Parameter.Specification.OPT; else if (p == InnerInterpreter.AMPKEY) state = Parameter.Specification.KEY; else if (p == InnerInterpreter.AMPREST) state = Parameter.Specification.REST; else { if (p is Symbol) { ret.Parameters[param++] = new Parameter((Symbol)p, Parameter.Specification.REQ, null); ++ret.NumReq; } else if (p is Cons) { ret.Parameters[param] = new Parameter((Symbol)((Cons)p).First, Parameter.Specification.REQ, null); ret.Parameters[param].TypeSpec = InnerInterpreter.Eval(Cons.GetSecond((Cons)p), env); ++param; ++ret.NumReq; } } break; case Parameter.Specification.OPT: if (p == InnerInterpreter.AMPOPT) throw new LispException("&optional can appear only once in arg list"); else if (p == InnerInterpreter.AMPKEY) state = Parameter.Specification.KEY; else if (p == InnerInterpreter.AMPREST) state = Parameter.Specification.REST; else { if (p is Symbol) { ret.Parameters[param++] = new Parameter((Symbol)p, Parameter.Specification.OPT, null); ++ret.NumOpt; } else if (p is Cons) { ret.Parameters[param++] = new Parameter((Symbol)((Cons)p).First, Parameter.Specification.OPT, InnerInterpreter.Analyze(Cons.GetSecond((Cons)p), env, loc)); ++ret.NumOpt; } else throw new LispException("&optional parameters must be symbols or (symbol init-form)"); } break; case Parameter.Specification.KEY: if (p == InnerInterpreter.AMPOPT) throw new LispException("&optional must appear before &key in arg list"); else if (p == InnerInterpreter.AMPKEY) throw new LispException("&key can appear only once in arg list"); else if (p == InnerInterpreter.AMPREST) state = Parameter.Specification.REST; else { if (p is Symbol) { ret.Parameters[param] = new Parameter((Symbol)p, Parameter.Specification.KEY, null); ret.Parameters[param].Key = InnerInterpreter.Intern(":" + ((Symbol)p).Name); ++param; ++ret.NumKey; } else if (p is Cons) { ret.Parameters[param] = new Parameter((Symbol)((Cons)p).First, Parameter.Specification.KEY, InnerInterpreter.Analyze(Cons.GetSecond((Cons)p), env, loc)); ret.Parameters[param].Key = InnerInterpreter.Intern(":" + ((Symbol)((Cons)p).First).Name); ++param; ++ret.NumKey; } else throw new LispException("&key parameters must be symbols or (symbol init-form)"); } break; case Parameter.Specification.REST: if (p == InnerInterpreter.AMPOPT) throw new LispException("&optional must appear before &rest in arg list"); else if (p == InnerInterpreter.AMPKEY) throw new LispException("&key must appear before &rest in arg list"); else if (p == InnerInterpreter.AMPREST) throw new LispException("&rest can appear only once in arg list"); else { if (!(p is Symbol)) throw new LispException("&rest parameter must be a symbol"); else { if (ret.NumRest > 0) //already got a rest param throw new LispException("Only one &rest arg can be specified"); ret.Parameters[param++] = new Parameter((Symbol)p, Parameter.Specification.REST, null); ++ret.NumRest; } } break; } a = a.Rest; } return ret; }