IBoundExpr IUnboundExprVisitor<IBoundExpr>.Visit(AssignExpr expr)
        {
            var value = expr.Value.Accept(this);

            if (expr.Target is TupleExpr) throw new NotSupportedException("Assignment to a tuple must be simplified before binding.");

            // handle a name target: foo <- 3
            var nameTarget = expr.Target as NameExpr;
            if (nameTarget != null)
            {
                // see if it's a local
                if (Scope.Contains(nameTarget.Name))
                {
                    if (!Scope.IsMutable(nameTarget.Name)) throw new CompileException(expr.Position, "Cannot assign to immutable local.");

                    // direct assign to local
                    return new StoreExpr(new LocalsExpr(), Scope[nameTarget.Name], value);
                }

                // look for an assignment function
                return TranslateAssignment(nameTarget.Position, nameTarget.Name, nameTarget.TypeArgs, new UnitExpr(Position.None), value);
            }

            // handle a function apply target: Foo 1 <- 3  ==> Foo<- (1, 3)
            var callTarget = expr.Target as CallExpr;
            if (callTarget != null)
            {
                var callArg = callTarget.Arg.Accept(this);

                // see if it's a direct function call
                var funcName = callTarget.Target as NameExpr;
                if ((funcName != null) && !mContext.Compiler.IsLocal(mFunction, Scope, funcName.Name))
                {
                    // translate the call
                    return TranslateAssignment(callTarget.Position, funcName.Name, funcName.TypeArgs, callArg, value);
                }

                // not calling a function, so try to desugar to a __Call<-
                var desugaredCallTarget = callTarget.Target.Accept(this);
                var desugaredCallArg = new BoundTupleExpr(new IBoundExpr[] { desugaredCallTarget, callArg, value });

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

                if (call != null) return call;

                throw new CompileException(expr.Position, "Couldn't figure out what you're trying to do on the left side of an assignment.");
            }

            // if we got here, it's not a valid assignment expression
            throw new CompileException(expr.Position, "Cannot assign to " + expr.Target);
        }
        bool IBoundExprVisitor <bool> .Visit(BoundTupleExpr tuple)
        {
            // must visit in forward order to ensure that function arguments are
            // evaluated left to right
            foreach (var field in tuple.Fields)
            {
                field.Accept(this);
            }

            // create the structure
            Write(OpCode.Alloc, tuple.Fields.Count);

            return(true);
        }
示例#3
0
        public static IBoundExpr PrependArg(this IBoundExpr arg, IBoundExpr value)
        {
            if (arg is UnitExpr)
            {
                // no arg, so just use the value
                return(value);
            }

            var tuple = arg as BoundTupleExpr;

            if (tuple != null)
            {
                // multiple args, so just add another
                var newArg = new BoundTupleExpr(tuple.Fields);
                newArg.Fields.Insert(0, value);
                return(newArg);
            }

            // single arg, so create a tuple
            return(new BoundTupleExpr(new IBoundExpr[] { value, arg }));
        }
        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));
        }