public static void Bind(BindingContext context, Function function)
        {
            if (function.Type.Return == null) throw new InvalidOperationException("Can only bind functions whose type is already bound.");

            var scope = new Scope();

            // create a local slot for the arg if there is one
            if (function.Type.Parameter.Unbound != Decl.Unit)
            {
                scope.Define(context.NameGenerator.Generate(), Decl.Unit /* ignored */, false);
            }

            var binder = new FunctionBinder(function, context, scope);

            // bind the function
            function.Bind(context, binder);

            // make sure declared return type matches actual return type
            if (!DeclComparer.TypesMatch(function.Type.Return.Bound, function.Body.Bound.Type))
            {
                if (function.Body.Bound.Type == Decl.EarlyReturn)
                {
                    //### bob: should find the return expr
                    throw new CompileException(function.Position, "Unneeded explicit \"return\".");
                }
                else
                {
                    throw new CompileException(function.Position, String.Format("{0} is declared to return {1} but is returning {2}.",
                        function.Name, function.Type.Return.Bound, function.Body.Bound.Type));
                }
            }
        }
Beispiel #2
0
            public ICallable Instantiate(BindingContext context, IEnumerable <IBoundDecl> typeArgs,
                                         IBoundDecl argType)
            {
                // should have three args: an int, an array, and a value
                var argTuple = argType as BoundTupleType;

                if (argTuple == null)
                {
                    return(null);
                }

                // index
                if (argTuple.Fields[0] != Decl.Int)
                {
                    return(null);
                }

                // array
                var arrayType = argTuple.Fields[1] as BoundArrayType;

                if (arrayType == null)
                {
                    return(null);
                }

                // value
                if (!DeclComparer.TypesMatch(argTuple.Fields[2], arrayType.ElementType))
                {
                    return(null);
                }

                // make the intrinsic
                return(new Intrinsic("__Call<-", OpCode.StoreArray, FuncType.Create(argTuple, Decl.Unit)));
            }
        IBoundExpr IUnboundExprVisitor<IBoundExpr>.Visit(ArrayExpr expr)
        {
            var elementType = (IBoundDecl)null;

            if (expr.ElementType != null)
            {
                elementType = TypeBinder.Bind(mContext, expr.ElementType);
            }

            var elements = expr.Elements.Accept(this);

            // infer the type from the elements
            if (elementType == null)
            {
                var index = 0;
                foreach (var element in elements)
                {
                    if (elementType == null)
                    {
                        // take the type of the first
                        elementType = element.Type;
                    }
                    else
                    {
                        // make sure the others match
                        if (!DeclComparer.TypesMatch(elementType, element.Type))
                            throw new CompileException(expr.Position, String.Format("Array elements must all be the same type. Array is type {0}, but element {1} is type {2}.",
                                elementType, index, element.Type));
                    }

                    index++;
                }
            }

            // build a structure for the array
            var fields = new List<IBoundExpr>();
            fields.Add(new IntExpr(expr.Elements.Count));

            fields.AddRange(elements);

            return new BoundTupleExpr(fields,
                new BoundArrayType(elementType));
        }
        public ICallable Instantiate(BindingContext context, IEnumerable <IBoundDecl> typeArgs,
                                     IBoundDecl argType)
        {
            bool canInfer;
            var  genericContext = BuildContext(context,
                                               BaseType.Type.Parameter.Unbound,
                                               argType, ref typeArgs, out canInfer);

            // bail if we couldn't get the right number of type arguments
            if ((typeArgs == null) || (TypeParameters.Count != typeArgs.Count()))
            {
                return(null);
            }

            // create a new bound function type with the type arguments applied
            FuncType funcType = BaseType.Type.CloneFunc();

            TypeBinder.Bind(genericContext, funcType);

            // make sure the concrete argument types of this instance match what we're actually given
            if (!DeclComparer.TypesMatch(funcType.Parameter.Bound, argType))
            {
                return(null);
            }

            // create a new unbound function with the proper type
            Function instance = new Function(BaseType.Position, BaseType.BaseName,
                                             funcType, BaseType.ParamNames, BaseType.Body.Unbound, typeArgs, canInfer);

            instance.BindSearchSpace(BaseType.SearchSpace);

            // don't instantiate it multiple times
            // note that this must happen *before* the function is bound, in case the
            // newly instantiated generic function is recursive.
            context.Compiler.Functions.Add(instance);

            // bind it with the type arguments in context
            FunctionBinder.Bind(genericContext, instance);

            return(instance);
        }
        private bool TryInferParam(IBoundDecl argType)
        {
            // see if the parameter type on top of the stack is a generic type
            NamedType named = ParamType as NamedType;

            if (named == null)
            {
                return(false);
            }

            // see if the named type is a generic type (instead of an actual concrete named type)
            int typeParamIndex = mTypeParamNames.IndexOf(named.Name);

            if (typeParamIndex == -1)
            {
                return(false);
            }

            // it is, so infer it from the arg
            if (mTypeArguments[typeParamIndex] == null)
            {
                // first time inferring the arg
                mTypeArguments[typeParamIndex] = argType;
            }
            else
            {
                // already inferred, make sure it matches
                if (!DeclComparer.TypesMatch(mTypeArguments[typeParamIndex], argType))
                {
                    // can't infer the same type parameter to multiple different types
                    // example: Foo'A (a A, b A)
                    //          Foo    (123, "string")
                    mFailed = true;
                }
            }

            return(true);
        }
        IBoundExpr IUnboundExprVisitor<IBoundExpr>.Visit(IfExpr expr)
        {
            var bound = new BoundIfExpr(
                expr.Condition.Accept(this),
                expr.ThenBody.Accept(this),
                (expr.ElseBody == null) ? null : expr.ElseBody.Accept(this));

            if (bound.Condition.Type != Decl.Bool)
            {
                throw new CompileException(expr.Position, String.Format(
                    "Condition of if/then/else is returning type {0} but should be Bool.",
                    bound.Condition.Type));
            }

            if (bound.ElseBody != null)
            {
                // has an else, so make sure both arms match
                if (!DeclComparer.TypesMatch(bound.ThenBody.Type, bound.ElseBody.Type))
                {
                    throw new CompileException(expr.Position, String.Format(
                        "Branches of if/then/else do not return the same type. Then arm returns {0} while else arm returns {1}.",
                        bound.ThenBody.Type, bound.ElseBody.Type));
                }
            }
            else
            {
                // no else, so make the then body doesn't return a value
                if ((bound.ThenBody.Type != Decl.Unit) && (bound.ThenBody.Type != Decl.EarlyReturn))
                {
                    throw new CompileException(expr.Position, String.Format(
                        "Body of if/then is returning type {0} but must be Unit (or a return) if there is no else branch.",
                        bound.ThenBody.Type));
                }
            }

            return bound;
        }
        IBoundExpr IUnboundExprVisitor<IBoundExpr>.Visit(CallExpr expr)
        {
            var namedTarget = expr.Target as NameExpr;

            // see if it's a macro call before binding the arg
            if ((namedTarget != null) && (mContext.Compiler.MacroProcessor != null))
            {
                IUnboundExpr macroResult = mContext.Compiler.MacroProcessor.Process(namedTarget.Name, expr.Arg);

                // if it was a macro call, bind the result of it
                if (macroResult != null) return macroResult.Accept(this);
            }

            //### bob: handle array constructors. hack! should be intrinsic
            if ((namedTarget != null) && (namedTarget.Name == "ArrayOf"))
            {
                // handle ArrayOf[Int]
                if ((expr.Arg is UnitExpr) && (namedTarget.TypeArgs.Count == 1))
                {
                    return new ArrayExpr(namedTarget.Position, namedTarget.TypeArgs[0]).Accept(this);
                }

                // handle ArrayOf (1, 2, 3)
                var elements = (IEnumerable<IUnboundExpr>)(new IUnboundExpr[] { expr.Arg });
                if (expr.Arg is TupleExpr)
                {
                    elements = ((TupleExpr)expr.Arg).Fields;
                }

                return new ArrayExpr(namedTarget.Position, elements).Accept(this);
            }

            var boundArg = expr.Arg.Accept(this);

            // see if we're accessing a record field
            BoundRecordType recordType = boundArg.Type as BoundRecordType;
            if ((namedTarget != null) &&
                (recordType != null) &&
                recordType.Fields.ContainsKey(namedTarget.Name))
            {
                // find the index of the field
                //### bob: ToList() here is a gross hack.
                var index = recordType.Fields.Keys.ToList().IndexOf(namedTarget.Name);

                // bind it
                return new LoadExpr(boundArg, recordType.Fields[namedTarget.Name], index);
            }

            if (namedTarget != null)
            {
                return mContext.ResolveName(mFunction, Scope, namedTarget.Position,
                    namedTarget.Name, namedTarget.TypeArgs, boundArg);
            }

            IBoundExpr target = expr.Target.Accept(this);

            // see if we're calling a function
            FuncType funcType = target.Type as FuncType;
            if (funcType != null)
            {
                // check that args match
                if (!DeclComparer.TypesMatch(funcType.Parameter.Bound, boundArg.Type))
                {
                    throw new CompileException(expr.Position, "Argument types passed to evaluated function reference do not match function's parameter types.");
                }

                // simply apply the arg to the bound expression
                return new BoundCallExpr(target, boundArg);
            }

            // see if we're accessing a tuple field
            var tupleType = boundArg.Type as BoundTupleType;
            if ((tupleType != null) && (target.Type == Decl.Int))
            {
                var index = target as IntExpr;
                if (index == null) throw new CompileException(expr.Position, "Tuple fields can only be accessed using a literal index, not an int expression.");

                // make sure the field is in range
                if ((index.Value < 0) || (index.Value >= tupleType.Fields.Count))
                    throw new CompileException(expr.Position, String.Format("Cannot access field {0} because the tuple only has {1} fields.", index.Value, tupleType.Fields.Count));

                // bind it
                return new LoadExpr(boundArg, tupleType.Fields[index.Value], index.Value);
            }

            // not calling a function, so try to desugar to a __Call
            var callArg = new BoundTupleExpr(new IBoundExpr[] { target, boundArg });

            var call = mContext.ResolveFunction(mFunction, expr.Target.Position,
                "__Call", new IUnboundDecl[0], callArg);

            if (call != null) return call;

            throw new CompileException(expr.Position, "Target of call is not a function.");
        }
        /// <summary>
        /// Resolves and binds a reference to a name.
        /// </summary>
        /// <param name="function">The function being compiled.</param>
        /// <param name="scope">The scope in which the name is being bound.</param>
        /// <param name="name">The name being resolved. May or may not be fully-qualified.</param>
        /// <param name="typeArgs">The type arguments being applied to the name. For
        /// example, resolving "foo'(int, bool)" would pass in {int, bool} here.</param>
        /// <param name="arg">The argument being applied to the name.</param>
        /// <returns></returns>
        public IBoundExpr ResolveName(Function function,
                                      Scope scope, Position position, string name,
                                      IList <IUnboundDecl> typeArgs, IBoundExpr arg)
        {
            IBoundDecl argType = null;

            if (arg != null)
            {
                argType = arg.Type;
            }

            IBoundExpr resolved = null;

            // see if it's an argument
            if (function.ParamNames.Contains(name))
            {
                // load the argument
                resolved = new LoadExpr(new LocalsExpr(), function.ParameterType, 0);

                if (function.ParamNames.Count > 1)
                {
                    // function takes multiple parameters, so load it from the tuple
                    var paramTuple = (BoundTupleType)function.ParameterType;

                    var argIndex = (byte)function.ParamNames.IndexOf(name);
                    resolved = new LoadExpr(resolved, paramTuple.Fields[argIndex], argIndex);
                }
            }

            // see if it's a local
            if (scope.Contains(name))
            {
                var local = scope[name];

                // just load the value
                resolved = new LoadExpr(new LocalsExpr(), scope[name]);
            }

            // if we resolved to a local name, handle it
            if (resolved != null)
            {
                if (typeArgs.Count > 0)
                {
                    throw new CompileException(position, "Cannot apply type arguments to a local variable or function argument.");
                }

                // if the local or argument is holding a function reference and we're passed args, call it
                if (argType != null)
                {
                    var funcType = resolved.Type as FuncType;

                    if (funcType != null)
                    {
                        // check that args match
                        if (!DeclComparer.TypesMatch(funcType.Parameter.Bound, argType))
                        {
                            throw new CompileException(position, "Argument types passed to local function reference do not match function's parameter types.");
                        }

                        // call it
                        resolved = new BoundCallExpr(resolved, arg);
                    }
                    else
                    {
                        // not calling a function, so try to desugar to a __Call
                        var callArg = new BoundTupleExpr(new IBoundExpr[] { resolved, arg });

                        resolved = ResolveFunction(function, position,
                                                   "__Call", new IUnboundDecl[0], callArg);

                        if (resolved == null)
                        {
                            throw new CompileException(position, "Cannot call a local variable or argument that is not a function reference, and could not find a matching __Call.");
                        }
                    }
                }

                return(resolved);
            }

            // implicitly apply () as the argument if no other argument was provided.
            // note that we do this *after* checking for locals because locals aren't
            // implicitly applied. since most locals aren't functions anyway, it won't
            // matter in most cases, and in cases where a local holds a function, the
            // user will mostly likely want to treat that function like a value: return
            // it, pass it around, etc.
            if (arg == null)
            {
                arg     = new UnitExpr(Position.None);
                argType = arg.Type;
            }

            return(ResolveFunction(function, position, name, typeArgs, arg));
        }