bool IBoundDeclVisitor <bool> .Visit(BoundRecordType decl) { if (!TryInferParam(decl)) { RecordType paramDecl = ParamType as RecordType; if (paramDecl == null) { mFailed = true; return(false); } if (paramDecl.Fields.Count != decl.Fields.Count) { mFailed = true; return(false); } foreach (var pair in paramDecl.Fields.Zip(decl.Fields)) { // field names must match if (pair.Item1.Key != pair.Item2.Key) { mFailed = true; return(false); } mParamTypes.Push(pair.Item1.Value); pair.Item2.Value.Accept(this); mParamTypes.Pop(); } } return(false); }
IBoundExpr IUnboundExprVisitor<IBoundExpr>.Visit(RecordExpr expr) { //### bob: there's a bug here. this will convert the record into a // tuple where the fields are ordered by name, not by how the appear // in the source code. when the tuple is then compiled, the fields // will be evaluated in that order. this violates the left-to-right // evaluation a user would expect. for example: // // Foo (->) Print "foo" // Bar (->) Print "bar" // Main (->) // def a <- (y: Foo x: Bar) // end // // this will print "bar" then "foo". // bind the fields var fields = new Dictionary<string, IBoundExpr>(); foreach (var field in expr.Fields) { fields.Add(field.Key, field.Value.Accept(this)); } // determine the record type var fieldTypes = new Dictionary<string, IBoundDecl>(); foreach (var field in fields) { fieldTypes.Add(field.Key, field.Value.Type); } var boundType = new BoundRecordType(fieldTypes); // discard the names and convert to just a struct // note that this assumes the fields will be correctly // iterated in sorted order return new BoundTupleExpr(fields.Values, boundType); }
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."); }