Exemplo n.º 1
0
        public static Expression CompileLambdaDef(Symbol name, Cons forms, AnalysisScope scope, LambdaKind kind, out string doc)
        {
            CheckMinLength(forms, 0);

            if (Length(forms) == 1)
            {
                PrintWarning("Function body contains no forms.");
            }

            var args = (Cons)First(forms);
            var body = Cdr(forms);
            var funscope = new AnalysisScope(scope, name == null ? null : name.Name);
            funscope.IsLambda = true;
            var template = new LambdaDefinition();
            template.Name = name;
            template.Signature = CompileFormalArgs(args, scope, kind);

            if (kind == LambdaKind.Method)
            {
                var container = name.Value as MultiMethod;
                if (container != null)
                {
                    var m = template.Signature;
                    var g = container.Signature;
                    var m1 = m.RequiredArgsCount;
                    var m2 = m.Parameters.Count;
                    var m3 = m.ArgModifier;
                    var g1 = g.RequiredArgsCount;
                    var g2 = g.Parameters.Count;
                    var g3 = g.ArgModifier;

                    if (m1 != g1)
                    {
                        throw new LispException("Method does not match multi-method: number of required arguments");
                    }
                    if (m3 != g3)
                    {
                        throw new LispException("Method does not match multi-method: different argument modifiers");
                    }
                    if (g3 != Symbols.Key && m2 != g2)
                    {
                        throw new LispException("Method does not match multi-method: number of arguments");
                    }
                    if (g3 == Symbols.Key)
                    {
                        // Replace keyword parameters with the full list from the generic definition, but keep the defaults
                        var usedKeys = template.Signature.Parameters.GetRange(m1, m2 - m1);
                        var replacementKeys = container.Signature.Parameters.GetRange(g1, g2 - g1);
                        template.Signature.Parameters.RemoveRange(m1, m2 - m1);
                        template.Signature.Names.RemoveRange(m1, m2 - m1);
                        foreach (var par in replacementKeys)
                        {
                            var oldpar = usedKeys.FirstOrDefault(x => x.Sym == par.Sym);
                            if (oldpar != null)
                            {
                                var newpar = new ParameterDef(par.Sym, initForm: oldpar.InitForm);
                                template.Signature.Parameters.Add(newpar);
                                template.Signature.Names.Add(par.Sym);
                            }
                            else {
                                // Insert place holder
                                var newpar = new ParameterDef(par.Sym, hidden: true);
                                template.Signature.Parameters.Add(newpar);
                                template.Signature.Names.Add(newpar.Sym);
                            }
                        }
                    }
                }
            }
            if (name != null)
            {
                template.Syntax = MakeListStar(template.Name, args);
            }

            template.Source = MakeListStar(Symbols.Lambda, args, body);

            doc = "";

            if (body != null)
            {
                if (body.Car is string)
                {
                    doc = (string)body.Car;
                    if (body.Cdr != null)
                    {
                        body = body.Cdr;
                    }
                }
            }

            var lambdaListNative = funscope.DefineNativeLocal(Symbols.LambdaList, ScopeFlags.All);
            var selfNative = funscope.DefineNativeLocal(Symbols.Self, ScopeFlags.All);
            var argsNative = funscope.DefineNativeLocal(Symbols.Args, ScopeFlags.All);
            Expression code;

            var rawparams = template.Signature.ArgModifier == Symbols.RawParams;
            var names = GetLambdaArgumentNames(template.Signature);
            var temp = GenTemp();
            var body4 = FormatCode(LambdaTemplate, Symbols.Args, temp, GetDeclarations(rawparams, names, temp), body);
            code = Compile(body4, funscope);

            template.Proc = CompileToFunction3(code, lambdaListNative, selfNative, argsNative);

            return CallRuntime(MakeLambdaClosureMethod, Expression.Constant(template, typeof(LambdaDefinition)));
        }
Exemplo n.º 2
0
        public static LambdaSignature CompileFormalArgs(Cons args, AnalysisScope scope, LambdaKind kind)
        {
            var signature = new LambdaSignature(kind);
            signature.ArgModifier = null;
            bool wantWholeArgName = false;
            bool wantEnvArgName = false;

            foreach (object item in ToIter(args))
            {
                if (wantWholeArgName)
                {
                    signature.WholeArg = (Symbol)item;
                    wantWholeArgName = false;
                }
                else if (wantEnvArgName)
                {
                    signature.EnvArg = (Symbol)item;
                    wantEnvArgName = false;
                }
                else if (item is Symbol)
                {
                    var sym = (Symbol)item;

                    if (sym == Symbols.Whole)
                    {
                        if (kind != LambdaKind.Macro)
                        {
                            throw new LispException("&whole parameter can only be used for a macro");
                        }
                        wantWholeArgName = true;
                    }
                    else if (sym == Symbols.Environment)
                    {
                        if (kind != LambdaKind.Macro)
                        {
                            throw new LispException("&environment parameter can only be used for a macro");
                        }
                        wantEnvArgName = true;
                    }
                    else if (sym == Symbols.Optional || sym == Symbols.Key || sym == Symbols.Rest
                             || sym == Symbols.Body || sym == Symbols.Params || sym == Symbols.Vector || sym == Symbols.RawParams)
                    {
                        if (signature.ArgModifier != null)
                        {
                            throw new LispException("Only one modifier can be used: &key, &optional, &rest, &body, &vector, &params or &rawparams");
                        }
                        signature.ArgModifier = sym;
                        signature.RequiredArgsCount = signature.Parameters.Count;
                        continue;
                    }
                    else {
                        var arg = new ParameterDef(sym);
                        signature.Parameters.Add(arg);
                        signature.Names.Add(sym);
                    }
                }
                else if (item is Cons)
                {
                    var list = (Cons)item;

                    if (signature.ArgModifier == Symbols.Key || signature.ArgModifier == Symbols.Optional)
                    {
                        var sym = (Symbol)First(list);
                        var initForm = Second(list);
                        if (initForm == null)
                        {
                            signature.Parameters.Add(new ParameterDef(sym));
                            signature.Names.Add(sym);
                        }
                        else {
                            var initForm2 = Compile(initForm, scope);
                            signature.Parameters.Add(new ParameterDef(sym, initForm: initForm2));
                            signature.Names.Add(sym);
                        }
                    }
                    else if (signature.ArgModifier == null && kind == LambdaKind.Macro)
                    {
                        var nestedArgs = CompileFormalArgs(list, scope, kind);
                        var arg = new ParameterDef(null, nestedParameters: nestedArgs);
                        signature.Parameters.Add(arg);
                        signature.Names.AddRange(nestedArgs.Names);
                    }
                    else if (signature.ArgModifier == null && kind == LambdaKind.Method)
                    {
                        var sym = (Symbol)First(list);
                        var type = Second(list);
                        if (type == null)
                        {
                            var arg = new ParameterDef(sym);
                            signature.Parameters.Add(arg);
                            signature.Names.Add(sym);
                        }
                        else if (type is Cons && First(type) == FindSymbol("lisp:eql"))
                        {
                            // Compile time constants!
                            var expr = Eval(Second(type));
                            var arg = new ParameterDef(sym, specializer: new EqlSpecializer(expr));
                            signature.Parameters.Add(arg);
                            signature.Names.Add(sym);
                        }
                        else {
                            if (!Symbolp(type) || Keywordp(type))
                            {
                                throw new LispException("Invalid type specifier: {0}", type);
                            }
                            var realType = GetType((Symbol)type);
                            var arg = new ParameterDef(sym, specializer: realType);
                            signature.Parameters.Add(arg);
                            signature.Names.Add(sym);
                        }
                    }
                    else {
                        throw new LispException("Invalid CONS in lambda parameter list");
                    }
                }
            }

            if (signature.ArgModifier == null)
            {
                signature.RequiredArgsCount = signature.Parameters.Count;
            }

            //if (kind == LambdaKind.Function)
            {
                var sym = signature.ArgModifier;
                if (sym == Symbols.RawParams)
                {
                    if (signature.Parameters.Count != 1)
                    {
                        throw new LispException("&rawparams: parameter count must be one.");
                    }
                }
                else if (sym == Symbols.Rest || sym == Symbols.Body || sym == Symbols.Params || sym == Symbols.Vector)
                {
                    if (signature.RequiredArgsCount + 1 != signature.Parameters.Count)
                    {
                        throw new LispException("Invalid placement of &rest or similar modifier.");
                    }
                }
            }

            return signature;
        }