예제 #1
0
        //.........................................................................
        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;
        }
예제 #2
0
        //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;
        }