
1 // 2 // Copyright (c) 2007, Brian Frank and Andy Frank 3 // Licensed under the Academic Free License version 3.0 4 // 5 // History: 6 // 20 Jan 07 Brian Frank Creation 7 // 8 9 ** 10 ** CurryResolver handles the process of resolving a CurryExpr. 11 ** 12 class CurryResolver : CompilerSupport 13 { 14 15 ////////////////////////////////////////////////////////////////////////// 16 // Construction 17 ////////////////////////////////////////////////////////////////////////// 18 19 ** 20 ** Constructor 21 ** 22 new make(Compiler compiler, TypeDef curType, Int curryCount, CurryExpr expr) 23 : super(compiler) 24 { 25 this.loc = expr.location 26 this.curType = curType 27 this.curryCount = curryCount 28 this.expr = expr 29 this.call = (CallExpr)expr.operand 30 this.method = call.method 31 } 32 33 ////////////////////////////////////////////////////////////////////////// 34 // Resolve 35 ////////////////////////////////////////////////////////////////////////// 36 37 ** 38 ** Resolve into a method call or field access 39 ** 40 Expr resolve() 41 { 42 try 43 { 44 // generate method signature 45 genSignature 46 47 // optimize static with no partials 48 if (method.isStatic && call.args.isEmpty) 49 return simpleStatic 50 51 // define curry type Curry$xx used to store partial 52 // arguments as fields and implement the curried method 53 defineCurry 54 return mapCurry 55 } 56 catch (CompilerErr err) 57 { 58 expr.ctype = ns.error 59 return expr 60 } 61 } 62 63 ////////////////////////////////////////////////////////////////////////// 64 // Signature 65 ////////////////////////////////////////////////////////////////////////// 66 67 ** 68 ** Define the signature of the curries method which 69 ** is any partial parameters left incomplete. 70 ** 71 Void genSignature() 72 { 73 // if more arguments were provided than parameters 74 if (call.args.size > method.params.size) 75 throw err("Too many arguments for curry", loc) 76 77 // get the incomplete CParams 78 incompletes := method.params[call.args.size..-1] 79 80 // map incomplete CParams to CTypes 81 params := (CType[])incompletes.map(CType[,]) |CParam p->CType| { return p.paramType } 82 83 // map incomplete CParams to names 84 names := (Str[])incompletes.map(Str[,]) |CParam p->Str| { return p.name } 85 86 // check if we have an instance method with no target, in that 87 // case then this must be passed as first argument to curried function 88 thisIsParam = !method.isStatic && (call.target == null || call.target.id == ExprId.staticTarget) 89 if (thisIsParam) 90 { 91 params.insert(0, method.parent) 92 names.insert(0, "\$this") 93 } 94 95 // define the signature 96 sig = FuncType.make(params, names, method.returnType) 97 } 98 99 ////////////////////////////////////////////////////////////////////////// 100 // Simple Static 101 ////////////////////////////////////////////////////////////////////////// 102 103 ** 104 ** If the curry is a static method with no arguments, then 105 ** really it isn't a curry per se, because we can optimize 106 ** to just a method lookup. 107 ** 108 Expr simpleStatic() 109 { 110 // replace curry with Slot.findMethod 111 result := CallExpr.makeWithMethod(loc, null, ns.slotFindFunc) 112 result.args.add(LiteralExpr.make(loc, ExprId.strLiteral, ns.strType, method.qname)) 113 result.ctype = sig 114 return result 115 } 116 117 ////////////////////////////////////////////////////////////////////////// 118 // Define Curry 119 ////////////////////////////////////////////////////////////////////////// 120 121 ** 122 ** Define a synthetic class called Curry$xx. 123 ** 124 Void defineCurry() 125 { 126 // define curry class 127 curry = TypeDef.make(ns, loc, curType.unit, "Curry\$${curryCount}") 128 curry.flags = FConst.Internal | FConst.Synthetic 129 curry.base = sig 130 addTypeDef(curry) 131 132 // define ctor 133 ctor = MethodDef.make(loc, curry) 134 ctor.flags = FConst.Ctor | FConst.Internal | FConst.Synthetic 135 ctor.name = "make" 136 ctor.ret = ns.voidType 137 ctor.code = Block.make(loc) 138 curry.addSlot(ctor) 139 } 140 141 ////////////////////////////////////////////////////////////////////////// 142 // Map Curry 143 ////////////////////////////////////////////////////////////////////////// 144 145 ** 146 ** For each argument specified we need to pass to the constructor 147 ** and stash away and in a field for use in redirecting to the 148 ** target method. 149 ** 150 Expr mapCurry() 151 { 152 // this is our redirect to the method call to be used in Method.call 153 doCall := CallExpr.makeWithMethod(loc, null, method); 154 155 // these are the arguments to provided to curried function 156 args := call.args.dup 157 allArgsConst := true 158 159 // figure out if there is an implied this parameter to curry, if 160 // so then we need to include this in the implied arguments 161 Expr self := null 162 if (!method.isStatic && call.target != null && call.target.id != ExprId.staticTarget) 163 args.insert(0, self = call.target) 164 165 // for each partial argument: 166 // - add field to curry class 167 // - add parameter to constructor 168 // - store field in constructor 169 // - load field in doCall 170 args.each |Expr expr, Int i| 171 { 172 // name and type of argument 173 name := "p$i" 174 ctype := expr.ctype 175 176 // keep track if all our args are const 177 if (!ctype.isConst) 178 allArgsConst = false 179 180 // add field to curry class 181 field := FieldDef.make(loc, curry) 182 field.name = name 183 field.flags = FConst.Internal | FConst.Storage | FConst.Synthetic 184 field.fieldType = ctype 185 curry.addSlot(field) 186 187 // add parameter to ctor 188 ctor.params.add(ParamDef.make(loc, ctype, name)) 189 190 // add field set assignment statement in ctor 191 lhs := FieldExpr.make(loc, ThisExpr.make(loc), field, false) 192 rhs := UnknownVarExpr.make(loc, null, name) 193 assign := BinaryExpr.makeAssign(lhs, rhs) 194 ctor.code.stmts.add(assign.toStmt) 195 196 // add field get to doCall statement 197 loadField := FieldExpr.make(loc, ThisExpr.make(loc), field, false) 198 if (expr === self) 199 doCall.target = loadField 200 else 201 doCall.args.add(loadField) 202 } 203 204 // finish ctor 205 ctor.code.stmts.add(ReturnStmt.make(loc)) 206 207 // Method.callX() 208 InitClosures.genMethodCall(compiler, loc, curry, sig, doCall, thisIsParam) 209 210 // generate isImmutable method 211 InitClosures.genIsImmutableMethod(compiler, loc, curry, allArgsConst) 212 213 // replace curry with call to CurryXX.make 214 result := CallExpr.makeWithMethod(loc, null, ctor) 215 result.args = args 216 result.ctype = sig 217 return result 218 } 219 220 ////////////////////////////////////////////////////////////////////////// 221 // Fields 222 ////////////////////////////////////////////////////////////////////////// 223 224 private Location loc // location of curry expr 225 private TypeDef curType // current enclosing type 226 private Int curryCount // total number of curries in compilation unit 227 private CurryExpr expr // curry expr being resolved 228 private CallExpr call // call operand of curry 229 private CMethod method // target method 230 private FuncType sig // signature of result 231 private Bool thisIsParam // is the "this" target of method incomplete 232 private TypeDef curry // curry implementation class 233 private MethodDef ctor // curry implementation class ctor 234 235 }