logo

class

compiler::CurryResolver

sys::Obj
  compiler::CompilerSupport
    compiler::CurryResolver
   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  }