logo

class

compiler::InitEnum

sys::Obj
  compiler::CompilerSupport
    compiler::CompilerStep
      compiler::InitEnum
   1  //
   2  // Copyright (c) 2006, Brian Frank and Andy Frank
   3  // Licensed under the Academic Free License version 3.0
   4  //
   5  // History:
   6  //   16 Apr 06  Brian Frank  Creation
   7  //   22 Sep 06  Brian Frank  Ported from Java to Fan
   8  //
   9  
  10  **
  11  ** InitEnum is used to auto-generate EnumDefs into abstract
  12  ** syntax tree representation of the fields and method.
  13  **
  14  **
  15  class InitEnum : CompilerStep
  16  {
  17  
  18  //////////////////////////////////////////////////////////////////////////
  19  // Constructor
  20  //////////////////////////////////////////////////////////////////////////
  21  
  22    new make(Compiler compiler)
  23      : super(compiler)
  24    {
  25    }
  26  
  27  //////////////////////////////////////////////////////////////////////////
  28  // Run
  29  //////////////////////////////////////////////////////////////////////////
  30  
  31    override Void run()
  32    {
  33      log.debug("InitEnum")
  34      walk(types, VisitDepth.typeDef)
  35      bombIfErr
  36    }
  37  
  38    override Void visitTypeDef(TypeDef t)
  39    {
  40      if (!t.isEnum) return
  41  
  42      try
  43      {
  44        addCtor
  45        addFromStr
  46        t.addFacet(this, "simple", true)
  47  
  48        fields := FieldDef[,]
  49        t.enumDefs.each |EnumDef e| { fields.add(makeField(e)) }
  50        fields.add(makeValuesField)
  51  
  52        // add enum fields to beginning of type
  53        fields.each |FieldDef f, Int i| { t.addSlot(f, i) }
  54      }
  55      catch (CompilerErr e)
  56      {
  57      }
  58    }
  59  
  60  //////////////////////////////////////////////////////////////////////////
  61  // Make Ctor
  62  //////////////////////////////////////////////////////////////////////////
  63  
  64    **
  65    ** Add constructor or enhance existing constructor.
  66    **
  67    Void addCtor()
  68    {
  69      // our constructor definition
  70      MethodDef m :=  null
  71  
  72      // check if there are any existing constructors - there
  73      // can only be zero or one called make
  74      ctors := curType.methodDefs.findAll |MethodDef x->Bool| { return x.isCtor }
  75      ctors.each |MethodDef ctor|
  76      {
  77        if (ctor.name == "make")
  78          m = ctor
  79        else
  80          throw err("Enum constructor must be named 'make'", ctor.location)
  81      }
  82  
  83      // if we found an existing constructor, then error check it
  84      if (m != null)
  85      {
  86        if (!m.isPrivate)
  87          err("Enum constructor must be private", m.location)
  88  
  89        if (m.ctorChain != null)
  90          err("Enum constructor cannot call super constructor", m.location)
  91      }
  92  
  93      // if we didn't find an existing constructor, then
  94      // add a synthetic one
  95      if (m == null)
  96      {
  97        m = MethodDef.make(curType.location, curType)
  98        m.name = "make"
  99        m.flags = FConst.Ctor | FConst.Private | FConst.Synthetic
 100        m.ret = TypeRef.make(curType.location, ns.voidType)
 101        m.code = Block.make(curType.location)
 102        m.code.stmts.add(ReturnStmt.make(curType.location))
 103        curType.addSlot(m)
 104      }
 105  
 106      // Enum.make call
 107      loc := m.location
 108      m.ctorChain = CallExpr.make(loc, SuperExpr.make(loc), "make")
 109      m.ctorChain.isCtorChain = true
 110      m.ctorChain.args.add(UnknownVarExpr.make(loc, null, "\$ordinal"))
 111      m.ctorChain.args.add(UnknownVarExpr.make(loc, null, "\$name"))
 112  
 113      // insert ordinal, name params
 114      m.params.insert(0, ParamDef.make(loc, ns.intType, "\$ordinal"))
 115      m.params.insert(1, ParamDef.make(loc, ns.strType, "\$name"))
 116    }
 117  
 118  //////////////////////////////////////////////////////////////////////////
 119  // Make FromStr
 120  //////////////////////////////////////////////////////////////////////////
 121  
 122    **
 123    ** Add fromStr method.
 124    **
 125    Void addFromStr()
 126    {
 127      // static CurType fromStr(Str name, Bool checked := true)
 128      loc := curType.location
 129      m := MethodDef.make(loc, curType)
 130      m.name = "fromStr"
 131      m.flags = FConst.Static | FConst.Public
 132      m.params.add(ParamDef.make(loc, ns.strType, "name"))
 133      m.params.add(ParamDef.make(loc, ns.boolType, "checked", LiteralExpr.make(loc, ExprId.trueLiteral, ns.boolType, true)))
 134      m.ret = TypeRef.make(loc, curType)
 135      m.code = Block.make(loc)
 136      m.doc  = ["Return the $curType.name instance for the specified name.  If not a",
 137                "valid name and checked is false return null, otherwise throw ParseErr."]
 138      curType.addSlot(m)
 139  
 140      // return (CurType)doParse(name, checked)
 141      doFromStr := CallExpr.make(loc, null, "doFromStr")
 142      doFromStr.args.add(LiteralExpr.make(loc, ExprId.typeLiteral, ns.typeType, curType))
 143      doFromStr.args.add(UnknownVarExpr.make(loc, null, "name"))
 144      doFromStr.args.add(UnknownVarExpr.make(loc, null, "checked"))
 145      cast := TypeCheckExpr.make(loc, ExprId.cast, doFromStr, curType)
 146      m.code.stmts.add(ReturnStmt.make(loc, cast))
 147    }
 148  
 149  //////////////////////////////////////////////////////////////////////////
 150  // Make Field
 151  //////////////////////////////////////////////////////////////////////////
 152  
 153    **
 154    ** Make enum value field:  public static final Foo name = make(ord, name)
 155    **
 156    FieldDef makeField(EnumDef def)
 157    {
 158      // ensure there isn't already a slot with same name
 159      dup := curType.slot(def.name)
 160      if (dup != null)
 161      {
 162        if (dup.parent === curType)
 163          throw err("Enum '$def.name' conflicts with slot", (Location)dup->location)
 164        else
 165          throw err("Enum '$def.name' conflicts with inherited slot '$dup.qname'", def.location)
 166      }
 167  
 168      loc := def.location
 169  
 170      // initializer
 171      init := CallExpr.make(loc, null, "make")
 172      init.args.add(LiteralExpr.make(loc, ExprId.intLiteral, ns.intType, def.ordinal))
 173      init.args.add(LiteralExpr.make(loc, ExprId.strLiteral, ns.strType, def.name))
 174      init.args.addAll(def.ctorArgs)
 175  
 176      // static field
 177      f := FieldDef.make(loc, curType)
 178      f.flags     = FConst.Public | FConst.Static | FConst.Const | FConst.Storage | FConst.Enum
 179      f.name      = def.name
 180      f.fieldType = curType
 181      f.init      = init
 182      return f
 183    }
 184  
 185    **
 186    ** Make values field: List of Enum values
 187    **
 188    FieldDef makeValuesField()
 189    {
 190      // ensure there isn't already a slot with same name
 191      dup := curType.slot("values")
 192      if (dup != null)
 193      {
 194        if (dup.parent == curType)
 195          throw err("Enum 'values' conflicts with slot", (Location)dup->location)
 196        else
 197          throw err("Enum 'values' conflicts with inherited slot '$dup.qname'", curType.location)
 198      }
 199  
 200      loc := curType.location
 201  
 202      // initializer
 203      listType := curType.toListOf
 204      list := ListLiteralExpr.make(loc, listType)
 205      curType.enumDefs.each |EnumDef e|
 206      {
 207        target := StaticTargetExpr.make(loc, curType)
 208        list.vals.add(UnknownVarExpr.make(loc, target, e.name))
 209      }
 210      init := CallExpr.make(loc, list, "toImmutable")
 211  
 212      // static field
 213      f := FieldDef.make(loc, curType)
 214      f.flags     = FConst.Public | FConst.Static | FConst.Const | FConst.Storage
 215      f.name      = "values"
 216      f.fieldType = listType
 217      f.init      = init
 218      f.doc       = ["List of $curType.name values indexed by ordinal"]
 219      return f
 220    }
 221  
 222  }