logo
class

compiler::SuperExpr

sys::Obj
  compiler::Node
    compiler::Expr
      compiler::LocalVarExpr
        compiler::SuperExpr
    1  //
    2  // Copyright (c) 2006, Brian Frank and Andy Frank
    3  // Licensed under the Academic Free License version 3.0
    4  //
    5  // History:
    6  //   19 Jul 06  Brian Frank  Creation
    7  //
    8  
    9  **
   10  ** Expr
   11  **
   12  abstract class Expr : Node
   13  {
   14  
   15  //////////////////////////////////////////////////////////////////////////
   16  // Construction
   17  //////////////////////////////////////////////////////////////////////////
   18  
   19    new make(Location location, ExprId id)
   20      : super(location)
   21    {
   22      this.id = id
   23    }
   24  
   25  //////////////////////////////////////////////////////////////////////////
   26  // Expr
   27  //////////////////////////////////////////////////////////////////////////
   28  
   29    **
   30    ** Return this expression as an Int literal usable in a tableswitch,
   31    ** or null if this Expr doesn't represent a constant Int.  Expressions
   32    ** which work as table switch cases: int literals and enum constants
   33    **
   34    virtual Int asTableSwitchCase() { return null }
   35  
   36    **
   37    ** Return if this expression matches the expected type.
   38    **
   39    Bool fits(CType expected)
   40    {
   41      // sanity check that this expression has been typed
   42      if (ctype == null) throw NullErr.make("null ctype: ${this}")
   43      actual := ctype
   44  
   45      // void is never any type
   46      if (actual.isVoid) return false
   47  
   48      // null can be used for any type (everything is boxed)
   49      if (id === ExprId.nullLiteral) return true
   50  
   51      // route to CType.fits
   52      return actual.fits(expected)
   53    }
   54  
   55    **
   56    ** Return if this expression can be used as the
   57    ** left hand side of an assignment expression.
   58    **
   59    virtual Bool isAssignable()
   60    {
   61      return false
   62    }
   63  
   64    **
   65    ** Is this a boolean conditional (boolOr/boolAnd)
   66    **
   67    virtual Bool isCond()
   68    {
   69      return false
   70    }
   71  
   72    **
   73    ** Does this expression make up a complete statement
   74    **
   75    virtual Bool isStmt()
   76    {
   77      return false
   78    }
   79  
   80    **
   81    ** Assignments to instance fields require a temporary local variable.
   82    **
   83    virtual Bool assignRequiresTempVar()
   84    {
   85      return false
   86    }
   87  
   88    **
   89    ** Map the list of expressions into their list of types
   90    **
   91    static CType[] ctypes(Expr[] exprs)
   92    {
   93      return (CType[])exprs.map(CType[,]) |Expr e->Obj| { return e.ctype }
   94    }
   95  
   96    **
   97    ** Given a list of Expr instances, find the common base type
   98    ** they all share.  This method does not take into account
   99    ** the null literal.  It is used for type inference for lists
  100    ** and maps.
  101    **
  102    static CType commonType(Namespace ns, Expr[] exprs)
  103    {
  104      exprs = exprs.exclude |Expr e->Bool| { return e.id == ExprId.nullLiteral }
  105      return CType.common(ns, ctypes(exprs))
  106    }
  107  
  108    **
  109    ** Return this expression as an ExprStmt
  110    **
  111    ExprStmt toStmt()
  112    {
  113      return ExprStmt.make(this)
  114    }
  115  
  116    **
  117    ** Return this expression as serialization text or
  118    ** throw exception if not serializable.
  119    **
  120    virtual Str serialize()
  121    {
  122      throw CompilerErr.make("'$id' not serializable", location)
  123    }
  124  
  125  //////////////////////////////////////////////////////////////////////////
  126  // Tree
  127  //////////////////////////////////////////////////////////////////////////
  128  
  129    Expr walk(Visitor v)
  130    {
  131      walkChildren(v)
  132      return v.visitExpr(this)
  133    }
  134  
  135    virtual Void walkChildren(Visitor v)
  136    {
  137    }
  138  
  139    static Expr walkExpr(Visitor v, Expr expr)
  140    {
  141      if (expr == null) return null
  142      return expr.walk(v)
  143    }
  144  
  145    static Expr[] walkExprs(Visitor v, Expr[] exprs)
  146    {
  147      for (i:=0; i<exprs.size; ++i)
  148      {
  149        expr := exprs[i]
  150        if (expr != null)
  151        {
  152          replace := expr.walk(v)
  153          if (expr !== replace)
  154            exprs[i] = replace
  155        }
  156      }
  157      return exprs
  158    }
  159  
  160  //////////////////////////////////////////////////////////////////////////
  161  // Debug
  162  //////////////////////////////////////////////////////////////////////////
  163  
  164    override abstract Str toStr()
  165  
  166    override Void print(AstWriter out)
  167    {
  168      out.w(toStr)
  169    }
  170  
  171  //////////////////////////////////////////////////////////////////////////
  172  // Fields
  173  //////////////////////////////////////////////////////////////////////////
  174  
  175    readonly ExprId id      // expression type identifier
  176    CType ctype             // type expression resolves to
  177    Bool leave := true      // leave this expression on the stack
  178  }
  179  
  180  **************************************************************************
  181  ** LiteralExpr
  182  **************************************************************************
  183  
  184  **
  185  ** LiteralExpr puts an Bool, Int, Float, Str, Duration, Uri,
  186  ** or null constant onto the stack.
  187  **
  188  class LiteralExpr : Expr
  189  {
  190    static LiteralExpr makeFor(Location loc, Namespace ns, Obj val)
  191    {
  192      switch (val.type)
  193      {
  194        case Bool.type:
  195          return val == true ?
  196            make(loc, ExprId.trueLiteral, ns.boolType, true) :
  197            make(loc, ExprId.falseLiteral, ns.boolType, false)
  198        default:
  199          throw Err.make("Unsupported literal type $val.type")
  200      }
  201    }
  202  
  203    new make(Location location, ExprId id, CType ctype, Obj val)
  204      : super(location, id)
  205    {
  206      this.ctype = ctype
  207      this.val   = val
  208    }
  209  
  210    override Int asTableSwitchCase()
  211    {
  212      return val as Int
  213    }
  214  
  215    override Str serialize()
  216    {
  217      switch (id)
  218      {
  219        case ExprId.falseLiteral:    return "false"
  220        case ExprId.trueLiteral:     return "true"
  221        case ExprId.intLiteral:      return val.toStr
  222        case ExprId.floatLiteral:    return val.toStr
  223        case ExprId.strLiteral:      return val.toStr.toCode
  224        case ExprId.uriLiteral:      return val.toStr.toCode('`')
  225        case ExprId.durationLiteral: return val.toStr
  226        default:                     return super.serialize
  227      }
  228    }
  229  
  230    override Str toStr()
  231    {
  232      switch (id)
  233      {
  234        case ExprId.nullLiteral: return "null"
  235        case ExprId.strLiteral:  return "\"" + val.toStr.replace("\n""\\n") + "\""
  236        case ExprId.typeLiteral: return "${val}.type"
  237        case ExprId.uriLiteral:  return "`$`"val
  238        defaultreturn val.toStr
  239      }
  240    }
  241  
  242    Obj val // Bool, Int, Float, Str (for Str/Uri), Duration, CType, or null
  243  }
  244  
  245  **************************************************************************
  246  ** RangeLiteralExpr
  247  **************************************************************************
  248  
  249  **
  250  ** RangeLiteralExpr creates a Range instance
  251  **
  252  class RangeLiteralExpr : Expr
  253  {
  254    new make(Location location, CType ctype)
  255      : super(location, ExprId.rangeLiteral)
  256    {
  257      this.ctype = ctype
  258    }
  259  
  260    override Void walkChildren(Visitor v)
  261    {
  262      start = start.walk(v)
  263      end   = end.walk(v)
  264    }
  265  
  266    override Str toStr()
  267    {
  268      if (exclusive)
  269        return "${start}...${end}"
  270      else
  271        return "${start}..${end}"
  272    }
  273  
  274    Expr start
  275    Expr end
  276    Bool exclusive
  277  }
  278  
  279  **************************************************************************
  280  ** ListLiteralExpr
  281  **************************************************************************
  282  
  283  **
  284  ** ListLiteralExpr creates a List instance
  285  **
  286  class ListLiteralExpr : Expr
  287  {
  288    new make(Location location, ListType explicitType := null)
  289      : super(location, ExprId.listLiteral)
  290    {
  291      this.explicitType = explicitType
  292    }
  293  
  294    override Void walkChildren(Visitor v)
  295    {
  296      vals = walkExprs(v, vals)
  297    }
  298  
  299    override Str serialize()
  300    {
  301      return format |Expr e->Str| { return e.serialize }
  302    }
  303  
  304    override Str toStr()
  305    {
  306      return format |Expr e->Str| { return e.toStr }
  307    }
  308  
  309    Str format(|Expr e->Str| f)
  310    {
  311      s := StrBuf.make
  312      if (explicitType != null) s.add(explicitType.v)
  313      s.add("[")
  314      if (vals.isEmpty) s.add(",")
  315      else vals.each |Expr v, Int i|
  316      {
  317        if (i > 0) s.add(",")
  318        s.add(f(v))
  319      }
  320      s.add("]")
  321      return s.toStr
  322    }
  323  
  324    ListType explicitType
  325    Expr[] vals := Expr[,]
  326  }
  327  
  328  **************************************************************************
  329  ** MapLiteralExpr
  330  **************************************************************************
  331  
  332  **
  333  ** MapLiteralExpr creates a List instance
  334  **
  335  class MapLiteralExpr : Expr
  336  {
  337    new make(Location location, MapType explicitType := null)
  338      : super(location, ExprId.mapLiteral)
  339    {
  340      this.explicitType = explicitType
  341    }
  342  
  343    override Void walkChildren(Visitor v)
  344    {
  345      keys = walkExprs(v, keys)
  346      vals = walkExprs(v, vals)
  347    }
  348  
  349    override Str serialize()
  350    {
  351      return format |Expr e->Str| { return e.serialize }
  352    }
  353  
  354    override Str toStr()
  355    {
  356      return format |Expr e->Str| { return e.toStr }
  357    }
  358  
  359    Str format(|Expr e->Str| f)
  360    {
  361      s := StrBuf.make
  362      if (explicitType != null) s.add(explicitType)
  363      s.add("[")
  364      if (vals.isEmpty) s.add(":")
  365      else
  366      {
  367        keys.size.times |Int i|
  368        {
  369          if (i > 0) s.add(",")
  370          s.add(f(keys[i])).add(":").add(f(vals[i]))
  371        }
  372      }
  373      s.add("]")
  374      return s.toStr
  375    }
  376  
  377    MapType explicitType
  378    Expr[] keys := Expr[,]
  379    Expr[] vals := Expr[,]
  380  }
  381  
  382  **************************************************************************
  383  ** SimpleLiteralExpr
  384  **************************************************************************
  385  
  386  **
  387  ** SimpleLiteralExpr is convenience for 'ctype.parse(arg)'
  388  **
  389  class SimpleLiteralExpr : Expr
  390  {
  391    new make(Location location, CType ctype, Expr arg)
  392      : super(location, ExprId.simpleLiteral)
  393    {
  394      this.ctype = ctype
  395      this.arg   = arg
  396    }
  397  
  398    override Void walkChildren(Visitor v)
  399    {
  400      arg = arg.walk(v)
  401    }
  402  
  403    override Str serialize()
  404    {
  405      return "${ctype}(${arg.serialize})"
  406    }
  407  
  408    override Str toStr()
  409    {
  410      return "${ctype}($arg)"
  411    }
  412  
  413    Expr arg            // str representation
  414    CMethod method      // parse method (resolved in CheckErrors)
  415  }
  416  
  417  **************************************************************************
  418  ** UnaryExpr
  419  **************************************************************************
  420  
  421  **
  422  ** UnaryExpr is used for unary expressions including !, +.
  423  ** Note that - is mapped to negate() as a shortcut method.
  424  **
  425  class UnaryExpr : Expr
  426  {
  427    new make(Location location, ExprId id, Token opToken, Expr operand)
  428      : super(location, id)
  429    {
  430      this.opToken = opToken
  431      this.operand = operand
  432    }
  433  
  434    override Void walkChildren(Visitor v)
  435    {
  436      operand = operand.walk(v)
  437    }
  438  
  439    override Str toStr()
  440    {
  441      if (id == ExprId.cmpNull)
  442        return operand.toStr + " == null"
  443      else if (id == ExprId.cmpNotNull)
  444        return operand.toStr + " != null"
  445      else
  446        return opToken.toStr + operand.toStr
  447    }
  448  
  449    Token opToken   // operator token type (Token.bang, etc)
  450    Expr operand    // operand expression
  451  
  452  }
  453  
  454  **************************************************************************
  455  ** BinaryExpr
  456  **************************************************************************
  457  
  458  **
  459  ** BinaryExpr is used for binary expressions with a left hand side and a
  460  ** right hand side including assignment.  Note that many common binary
  461  ** operations are actually modeled as ShortcutExpr to enable method based
  462  ** operator overloading.
  463  **
  464  class BinaryExpr : Expr
  465  {
  466    new make(Expr lhs, Token opToken, Expr rhs)
  467      : super(lhs.location, opToken.toExprId)
  468    {
  469      this.lhs = lhs
  470      this.opToken = opToken
  471      this.rhs = rhs
  472    }
  473  
  474    new makeAssign(Expr lhs, Expr rhs, Bool leave := false)
  475      : this.make(lhs, Token.assign, rhs)
  476    {
  477      this.ctype = lhs.ctype
  478      this.leave = leave
  479    }
  480  
  481    override Bool isStmt() { return id === ExprId.assign }
  482  
  483    override Void walkChildren(Visitor v)
  484    {
  485      lhs = lhs.walk(v)
  486      rhs = rhs.walk(v)
  487    }
  488  
  489    override Str serialize()
  490    {
  491      if (id === ExprId.assign)
  492        return "${lhs.serialize}=${rhs.serialize}"
  493      else
  494        return super.serialize
  495    }
  496  
  497    override Str toStr()
  498    {
  499      return "($lhs $opToken $rhs)"
  500    }
  501  
  502    Token opToken      // operator token type (Token.and, etc)
  503    Expr lhs           // left hand side
  504    Expr rhs           // right hand side
  505    MethodVar tempVar  // temp local var to store field assignment leaves
  506  
  507  }
  508  
  509  **************************************************************************
  510  ** CondExpr
  511  **************************************************************************
  512  
  513  **
  514  ** CondExpr is used for || and && short-circuit boolean conditionals.
  515  **
  516  class CondExpr : Expr
  517  {
  518    new make(Expr first, Token opToken)
  519      : super(first.location, opToken.toExprId)
  520    {
  521      this.opToken = opToken
  522      this.operands = [first]
  523    }
  524  
  525    override Bool isCond() { return true }
  526  
  527    override Void walkChildren(Visitor v)
  528    {
  529      operands = walkExprs(v, operands)
  530    }
  531  
  532    override Str toStr()
  533    {
  534      return operands.join(" $opToken ")
  535    }
  536  
  537    Token opToken      // operator token type (Token.and, etc)
  538    Expr[] operands    // list of operands
  539  
  540  }
  541  
  542  **************************************************************************
  543  ** NameExpr
  544  **************************************************************************
  545  
  546  **
  547  ** NameExpr is the base class for an identifier expression which has
  548  ** an optional base expression.  NameExpr is the base class for
  549  ** UnknownVarExpr and CallExpr which are resolved via CallResolver
  550  **
  551  abstract class NameExpr : Expr
  552  {
  553    new make(Location location, ExprId id, Expr target, Str name)
  554      : super(location, id)
  555    {
  556      this.target = target
  557      this.name   = name
  558    }
  559  
  560    override Void walkChildren(Visitor v)
  561    {
  562      target = walkExpr(v, target)
  563    }
  564  
  565    override Str toStr()
  566    {
  567      if (target != null)
  568        return "${target}.${name}"
  569      else
  570        return name
  571    }
  572  
  573    Expr target   // base target expression or null
  574    Str name      // name of variable (local/field/method)
  575  }
  576  
  577  **************************************************************************
  578  ** UnknownVarExpr
  579  **************************************************************************
  580  
  581  **
  582  ** UnknownVarExpr is a place holder in the AST for a variable until
  583  ** we can figure out what it references: local or slot.  We also use
  584  ** this class for storage operators before they are resolved to a field.
  585  **
  586  class UnknownVarExpr : NameExpr
  587  {
  588    new make(Location location, Expr target, Str name)
  589      : super(location, ExprId.unknownVar, target, name)
  590    {
  591    }
  592  
  593    new makeStorage(Location location, Expr target, Str name)
  594      : super.make(location, ExprId.storage, target, name)
  595    {
  596    }
  597  }
  598  
  599  **************************************************************************
  600  ** CallExpr
  601  **************************************************************************
  602  
  603  **
  604  ** CallExpr is a method call.
  605  **
  606  class CallExpr : NameExpr
  607  {
  608    new make(Location location, Expr target := null, Str name := null, ExprId id := ExprId.call)
  609      : super(location, id, target, name)
  610    {
  611      args = Expr[,]
  612      isDynamic = false
  613      isCtorChain = false
  614    }
  615  
  616    new makeWithMethod(Location location, Expr target, CMethod method, Expr[] args := null)
  617      : this.make(location, target, method.name, ExprId.call)
  618    {
  619      this.method = method
  620  
  621      if (args != null)
  622        this.args = args
  623  
  624      if (method.isCtor)
  625        ctype = method.parent
  626      else
  627        ctype = method.returnType
  628    }
  629  
  630    override Str toStr()
  631    {
  632      return toCallStr(true)
  633    }
  634  
  635    override Bool isStmt() { return true }
  636  
  637    override Void walkChildren(Visitor v)
  638    {
  639      target = walkExpr(v, target)
  640      args = walkExprs(v, args)
  641    }
  642  
  643    override Void print(AstWriter out)
  644    {
  645      out.w(toCallStr(false))
  646      if (args.size > 0 && args.last is ClosureExpr)
  647        args.last.print(out)
  648    }
  649  
  650    private Str toCallStr(Bool isToStr)
  651    {
  652      s := StrBuf.make
  653  
  654      if (target != null)
  655        s.add(target).add(isDynamic ? "->" : ".")
  656      else if (method != null && (method.isStatic || method.isCtor))
  657        s.add(method.parent.qname).add(".")
  658  
  659      s.add(name).add("(")
  660      if (args.last is ClosureExpr)
  661      {
  662        s.add(args[0..-2].join(", ")).add(") ");
  663        if (isToStr) s.add(args.last)
  664      }
  665      else
  666      {
  667        s.add(args.join(", ")).add(")")
  668      }
  669      return s.toStr
  670    }
  671  
  672    Expr[] args         // Expr[] arguments to pass
  673    Bool isDynamic      // true if this is a -> dynamic call
  674    Bool isCtorChain    // true if this is MethodDef.ctorChain call
  675    CMethod method      // resolved method
  676  }
  677  
  678  **************************************************************************
  679  ** ShortcutExpr
  680  **************************************************************************
  681  
  682  **
  683  ** ShortcutExpr is used for operator expressions which are a shortcut
  684  ** to a method call:
  685  **   a + b    -> a.plus(b)
  686  **   a - b    -> a.minus(b)
  687  **   a * b    -> a.star(b)
  688  **   a / b    -> a.slash(b)
  689  **   a % b    -> a.percent(b)
  690  **   a[b]     -> a.get(b)
  691  **   a[b] = c -> a.set(b, c)
  692  **   a[b]     -> a.slice(b) if b is Range
  693  **   a[b] = c -> a.splice(b, c) if b is Range
  694  **   a << b   -> a.lshift(b)
  695  **   a >> b   -> a.rshift(b)
  696  **   a & b    -> a.amp(b)
  697  **   a | b    -> a.pipe(b)
  698  **   a ^ b    -> a.caret(b)
  699  **   ~a       -> a.tilde()
  700  **   -a       -> a.negate()
  701  **   ++a, a++ -> a.increment()
  702  **   --a, a-- -> a.decrement()
  703  **   a == b   -> a.equals(b)
  704  **   a != b   -> ! a.equals(b)
  705  **   a <=>    -> a.compare(b)
  706  **   a > b    -> a.compare(b) > 0
  707  **   a >= b   -> a.compare(b) >= 0
  708  **   a < b    -> a.compare(b) < 0
  709  **   a <= b   -> a.compare(b) <= 0
  710  **
  711  class ShortcutExpr : CallExpr
  712  {
  713    new makeUnary(Location loc, Token opToken, Expr operand)
  714      : super.make(loc, nullnull, ExprId.shortcut)
  715    {
  716      this.op      = opToken.toShortcutOp(1)
  717      this.opToken = opToken
  718      this.name    = op.methodName
  719      this.target  = operand
  720    }
  721  
  722    new makeBinary(Expr lhs, Token opToken, Expr rhs)
  723      : super.make(lhs.location, nullnull, ExprId.shortcut)
  724    {
  725      this.op      = opToken.toShortcutOp(2)
  726      this.opToken = opToken
  727      this.name    = op.methodName
  728      this.target  = lhs
  729      this.args.add(rhs)
  730    }
  731  
  732    new makeGet(Location loc, Expr target, Expr index)
  733      : super.make(loc, nullnull, ExprId.shortcut)
  734    {
  735      this.op      = ShortcutOp.get
  736      this.opToken = Token.lbracket
  737      this.name    = op.methodName
  738      this.target  = target
  739      this.args.add(index)
  740    }
  741  
  742    new makeFrom(ShortcutExpr from)
  743      : super.make(from.location, nullnull, ExprId.shortcut)
  744    {
  745      this.op      = from.op
  746      this.opToken = from.opToken
  747      this.name    = from.name
  748      this.target  = from.target
  749      this.args    = from.args
  750      this.isPostfixLeave = from.isPostfixLeave
  751    }
  752  
  753    override Bool assignRequiresTempVar()
  754    {
  755      return isAssignable
  756    }
  757  
  758    override Bool isAssignable()
  759    {
  760      return op == ShortcutOp.get
  761    }
  762  
  763    override Bool isStmt() { return isAssign || op === ShortcutOp.set }
  764  
  765    Bool isAssign() { return opToken.isAssign }
  766  
  767    Bool isStrConcat()
  768    {
  769      return opToken == Token.plus && (target.ctype.isStr || args.first.ctype.isStr)
  770    }
  771  
  772    override Str toStr()
  773    {
  774      if (op == ShortcutOp.get) return "${target}[$args.first]"
  775      if (op == ShortcutOp.increment) return isPostfixLeave ? "${target}++" : "++${target}"
  776      if (op == ShortcutOp.decrement) return isPostfixLeave ? "${target}--" : "--${target}"
  777      if (isAssign) return "${target} ${opToken} ${args.first}"
  778      if (op.degree == 1) return "${opToken}${target}"
  779      if (op.degree == 2) return "(${target} ${opToken} ${args.first})"
  780      return super.toStr
  781    }
  782  
  783    override Void print(AstWriter out)
  784    {
  785      out.w(toStr())
  786    }
  787  
  788    ShortcutOp op
  789    Token opToken
  790    Bool isPostfixLeave := false  // x++ or x-- (must have Expr.leave set too)
  791    MethodVar tempVar    // temp local var to store += to field/indexed
  792  }
  793  
  794  **
  795  ** IndexedAssignExpr is a subclass of ShortcutExpr used
  796  ** in situations like x[y] += z where we need keep of two
  797  ** extra scratch variables and the get's matching set method.
  798  ** Note this class models the top x[y] += z, NOT the get target
  799  ** which is x[y].
  800  **
  801  ** In this example, IndexedAssignExpr shortcuts Int.plus and
  802  ** its target shortcuts List.get:
  803  **   x := [2]
  804  **   x[0] += 3
  805  **
  806  class IndexedAssignExpr : ShortcutExpr
  807  {
  808    new makeFrom(ShortcutExpr from)
  809      : super.makeFrom(from)
  810    {
  811    }
  812  
  813    MethodVar scratchA
  814    MethodVar scratchB
  815    CMethod setMethod
  816  }
  817  
  818  **************************************************************************
  819  ** FieldExpr
  820  **************************************************************************
  821  
  822  **
  823  ** FieldExpr is used for a field variable access.
  824  **
  825  class FieldExpr : Expr
  826  {
  827    new make(Location location, Expr target := null, CField field := null, Bool useAccessor := true)
  828      : super(location, ExprId.field)
  829    {
  830      this.target = target
  831      this.useAccessor = useAccessor
  832      if (field != null)
  833      {
  834        this.name  = field.name
  835        this.field = field
  836        this.ctype = field.fieldType
  837      }
  838    }
  839  
  840    override Bool isAssignable() { return true }
  841  
  842    override Bool assignRequiresTempVar() { return !field.isStatic }
  843  
  844    override Int asTableSwitchCase()
  845    {
  846      // TODO - this should probably be tightened up if we switch to const
  847      if (field.isStatic && field.parent.isEnum && ctype.isEnum)
  848      {
  849        switch (field.type)
  850        {
  851          case ReflectField.type:
  852            ifield := field as ReflectField
  853            return ((Enum)ifield.f.get).ordinal
  854          case FieldDef.type:
  855            fieldDef := field as FieldDef
  856            enumDef := fieldDef.parentDef.enumDef(field.name)
  857            if (enumDef != null) return enumDef.ordinal
  858          default:
  859            throw CompilerErr.make("Invalid field for tableswitch: " + field.type, location)
  860        }
  861      }
  862      return null
  863    }
  864  
  865    override Void walkChildren(Visitor v)
  866    {
  867      target = walkExpr(v, target)
  868    }
  869  
  870    override Str serialize()
  871    {
  872      if (target != null && target.id === ExprId.withBase)
  873        return "$name"
  874  
  875      if (field.isStatic)
  876      {
  877        if (field.parent.isFloat)
  878        {
  879          switch (name)
  880          {
  881            case "nan":    return "sys::Float(\"NaN\")"
  882            case "posInf"return "sys::Float(\"INF\")"
  883            case "negInf"return "sys::Float(\"-INF\")"
  884          }
  885        }
  886  
  887        if (field.isEnum)
  888          return "${field.parent.qname}(\"$name\")"
  889      }
  890  
  891      return super.serialize
  892    }
  893  
  894    override Str toStr()
  895    {
  896      s := StrBuf.make
  897      if (target != null) s.add(target).add(".");
  898      if (!useAccessor) s.add("@")
  899      s.add(name)
  900      return s.toStr
  901    }
  902  
  903    Expr target         // target of field access
  904    Str name            // unresolved name of field to get/set
  905    CField field        // resolved field
  906    Bool useAccessor    // false if access using '@' storage operator
  907  }
  908  
  909  **************************************************************************
  910  ** LocalVarExpr
  911  **************************************************************************
  912  
  913  **
  914  ** LocalVarExpr is used to access a local variable stored in a register.
  915  **
  916  class LocalVarExpr : Expr
  917  {
  918    new make(Location location, MethodVar var, ExprId id := ExprId.localVar)
  919      : super(location, id)
  920    {
  921      if (var != null)
  922      {
  923        this.var = var
  924        this.ctype = var.ctype
  925      }
  926    }
  927  
  928    override Bool isAssignable() { return true }
  929  
  930    override Bool assignRequiresTempVar() { return var.usedInClosure }
  931  
  932    virtual Int register() { return var.register }
  933  
  934    override Str toStr()
  935    {
  936      if (var == null) return "???"
  937      return var.name
  938    }
  939  
  940    MethodVar var   // bound variable
  941  
  942    // used to mark a local var access that should not be
  943    // pulled out into cvars, even if var.usedInClosure is true
  944    Bool noRemapToCvars := false
  945  }
  946  
  947  **************************************************************************
  948  ** ThisExpr
  949  **************************************************************************
  950  
  951  **
  952  ** ThisExpr models the "this" keyword to access the implicit this
  953  ** local variable always stored in register zero.
  954  **
  955  class ThisExpr : LocalVarExpr
  956  {
  957    new make(Location location, CType ctype := null)
  958      : super(location, null, ExprId.thisExpr)
  959    {
  960      this.ctype = ctype
  961    }
  962  
  963    override Bool isAssignable() { return false }
  964  
  965    override Int register() { return 0 }
  966  
  967    override Str toStr()
  968    {
  969      return "this"
  970    }
  971  }
  972  
  973  **************************************************************************
  974  ** SuperExpr
  975  **************************************************************************
  976  
  977  **
  978  ** SuperExpr is used to access super class slots.  It always references
  979  ** the implicit this local variable stored in register zero, but the
  980  ** super class's slot definitions.
  981  **
  982  class SuperExpr : LocalVarExpr
  983  {
  984    new make(Location location, CType explicitType := null)
  985      : super(location, null, ExprId.superExpr)
  986    {
  987      this.explicitType = explicitType
  988    }
  989  
  990    override Bool isAssignable() { return false }
  991  
  992    override Int register() { return 0 }
  993  
  994    override Str toStr()
  995    {
  996      if (explicitType != null)
  997        return "${explicitType}.super"
  998      else
  999        return "super"
 1000    }
 1001  
 1002    CType explicitType   // if "named super"
 1003  }
 1004  
 1005  **************************************************************************
 1006  ** StaticTargetExpr
 1007  **************************************************************************
 1008  
 1009  **
 1010  ** StaticTargetExpr wraps a type reference as an Expr for use as
 1011  ** a target in a static field access or method call
 1012  **
 1013  class StaticTargetExpr : Expr
 1014  {
 1015    new make(Location location, CType ctype)
 1016      : super(location, ExprId.staticTarget)
 1017    {
 1018      this.ctype = ctype
 1019    }
 1020  
 1021    override Str toStr()
 1022    {
 1023      return ctype.signature
 1024    }
 1025  }
 1026  
 1027  **************************************************************************
 1028  ** TypeCheckExpr
 1029  **************************************************************************
 1030  
 1031  **
 1032  ** TypeCheckExpr is an expression which is composed of an arbitrary
 1033  ** expression and a type - is, as, & casts
 1034  **
 1035  class TypeCheckExpr : Expr
 1036  {
 1037    new make(Location location, ExprId id, Expr target, CType check)
 1038      : super(location, id)
 1039    {
 1040      this.target = target
 1041      this.check  = check
 1042    }
 1043  
 1044    new cast(Expr target, CType to)
 1045      : super.make(target.location, ExprId.cast)
 1046    {
 1047      this.target = target
 1048      this.check  = to
 1049    }
 1050  
 1051    override Void walkChildren(Visitor v)
 1052    {
 1053      target = walkExpr(v, target)
 1054    }
 1055  
 1056    override Str toStr()
 1057    {
 1058      switch (id)
 1059      {
 1060        case ExprId.isExpr: return "($target is $check)"
 1061        case ExprId.asExpr: return "($target as $check)"
 1062        case ExprId.cast:   return "($check)$target"
 1063        default:            throw Err.make(id.toStr)
 1064      }
 1065    }
 1066  
 1067    Expr target
 1068    CType check
 1069  }
 1070  
 1071  **************************************************************************
 1072  ** TernaryExpr
 1073  **************************************************************************
 1074  
 1075  **
 1076  ** TernaryExpr is used for the ternary expression <cond> ? <true> : <false>
 1077  **
 1078  class TernaryExpr : Expr
 1079  {
 1080    new make(Expr condition, Expr trueExpr, Expr falseExpr)
 1081      : super(condition.location, ExprId.ternary)
 1082    {
 1083      this.condition = condition
 1084      this.trueExpr  = trueExpr
 1085      this.falseExpr = falseExpr
 1086    }
 1087  
 1088    override Void walkChildren(Visitor v)
 1089    {
 1090      condition = condition.walk(v)
 1091      trueExpr  = trueExpr.walk(v)
 1092      falseExpr = falseExpr.walk(v)
 1093    }
 1094  
 1095    override Str toStr()
 1096    {
 1097      return "$condition ? $trueExpr : $falseExpr"
 1098    }
 1099  
 1100    Expr condition     // boolean test
 1101    Expr trueExpr      // result of expression if condition is true
 1102    Expr falseExpr     // result of expression if condition is false
 1103  }
 1104  
 1105  **************************************************************************
 1106  ** WithBlockExpr
 1107  **************************************************************************
 1108  
 1109  **
 1110  ** WithBlockExpr is used enclose a series of sub-expressions
 1111  ** against a base expression:
 1112  **   base { a = b; c() }
 1113  ** Translates to:
 1114  **   temp := base
 1115  **   temp.a = b
 1116  **   temp.c()
 1117  **
 1118  class WithBlockExpr : Expr
 1119  {
 1120    new make(Expr base)
 1121      : super(base.location, ExprId.withBlock)
 1122    {
 1123      this.base = base
 1124      this.subs = Expr[,]
 1125    }
 1126  
 1127    override Void walkChildren(Visitor v)
 1128    {
 1129      base  = base.walk(v)
 1130      ctype = base.ctype
 1131      subs  = walkExprs(v, subs)
 1132    }
 1133  
 1134    override Str serialize()
 1135    {
 1136      if (base.id != ExprId.call || base->method->isCtor != true ||
 1137          base->name != "make" || base->args->size != 0)
 1138        return super.serialize
 1139  
 1140      s := StrBuf.make
 1141      s.add("${base->target}{")
 1142      subs.each |Expr sub| { s.add("$sub.serialize;") }
 1143      s.add("}")
 1144      return s.toStr
 1145    }
 1146  
 1147    override Str toStr()
 1148    {
 1149      s := StrBuf.make
 1150      s.add("$base { ")
 1151      subs.each |Expr sub| { s.add("$sub; ") }
 1152      s.add("}")
 1153      return s.toStr
 1154    }
 1155  
 1156    Expr base      // base expression
 1157    Expr[] subs    // sub-expressions applied to base
 1158  }
 1159  
 1160  **
 1161  ** WithBaseExpr is a place holder used as the target of
 1162  ** sub-expressions within a with block typed to the with base.
 1163  **
 1164  class WithBaseExpr : Expr
 1165  {
 1166    new make(WithBlockExpr withBlock)
 1167      : super(withBlock.location, ExprId.withBase)
 1168    {
 1169      this.withBlock = withBlock
 1170    }
 1171  
 1172    override Str toStr()
 1173    {
 1174      return "with"
 1175    }
 1176  
 1177    override Void walkChildren(Visitor v)
 1178    {
 1179      // this node never has children, but whenever the
 1180      // tree is walked update its ctype from the withBlock
 1181      ctype = withBlock.ctype
 1182    }
 1183  
 1184    WithBlockExpr withBlock
 1185  }
 1186  
 1187  **************************************************************************
 1188  ** CurryExpr
 1189  **************************************************************************
 1190  
 1191  **
 1192  ** CurryExpr is used to "curry" a function into another
 1193  ** function thru partially evaluation
 1194  **
 1195  class CurryExpr : Expr
 1196  {
 1197    new make(Location location, Expr operand)
 1198      : super(location, ExprId.curry)
 1199    {
 1200      this.operand = operand
 1201    }
 1202  
 1203    override Void walkChildren(Visitor v)
 1204    {
 1205      operand = operand.walk(v)
 1206    }
 1207  
 1208    override Str toStr()
 1209    {
 1210      return "&$operand"
 1211    }
 1212  
 1213    Expr operand
 1214  }
 1215  
 1216  **************************************************************************
 1217  ** ClosureExpr
 1218  **************************************************************************
 1219  
 1220  **
 1221  ** ClosureExpr is an "inlined anonymous method" which closes over it's
 1222  ** lexical scope.  ClosureExpr is placed into the AST by the parser
 1223  ** with the code field containing the method implementation.  In
 1224  ** InitClosures we remap a ClosureExpr to an anonymous class TypeDef
 1225  ** which extends Func.  The function implementation is moved to the
 1226  ** anonymous class's doCall() method.  However we leave ClosureExpr
 1227  ** in the AST in it's original location with a substitute expression.
 1228  ** The substitute expr just creates an instance of the anonymous class.
 1229  ** But by leaving the ClosureExpr in the tree, we can keep track of
 1230  ** the original lexical scope of the closure.
 1231  **
 1232  class ClosureExpr : Expr
 1233  {
 1234    new make(Location location, TypeDef enclosingType,
 1235             MethodDef enclosingMethod, ClosureExpr enclosingClosure,
 1236             FuncType signature, Str name)
 1237      : super(location, ExprId.closure)
 1238    {
 1239      this.ctype            = signature
 1240      this.enclosingType    = enclosingType
 1241      this.enclosingMethod  = enclosingMethod
 1242      this.enclosingClosure = enclosingClosure
 1243      this.signature        = signature
 1244      this.name             = name
 1245      this.code             = code
 1246      this.usesCvars        = false
 1247    }
 1248  
 1249    readonly CField outerThisField
 1250    {
 1251      get
 1252      {
 1253        if (@outerThisField == null)
 1254        {
 1255          if (enclosingMethod.isStatic) throw Err.make("Internal error: $location.toLocationStr")
 1256          @outerThisField = ClosureVars.makeOuterThisField(this)
 1257        }
 1258        return @outerThisField
 1259      }
 1260    }
 1261  
 1262    override Str toStr()
 1263    {
 1264      return "$signature { ... }"
 1265    }
 1266  
 1267    override Void print(AstWriter out)
 1268    {
 1269      out.w(signature.toStr)
 1270      if (substitute != null)
 1271      {
 1272        out.w(" { substitute: ")
 1273        substitute.print(out)
 1274        out.w(" }").nl
 1275      }
 1276      else
 1277      {
 1278        out.nl
 1279        code.print(out)
 1280      }
 1281    }
 1282  
 1283    // Parse
 1284    TypeDef enclosingType         // enclosing class
 1285    MethodDef enclosingMethod     // enclosing method
 1286    ClosureExpr enclosingClosure  // if nested closure
 1287    FuncType signature            // parameter and return signature
 1288    Block code                    // moved into a MethodDef in InitClosures
 1289    Str name                      // anonymous class name
 1290  
 1291    // InitClosures
 1292    CallExpr substitute           // expression to substitute during assembly
 1293    TypeDef cls                   // anonymous class which implements the closure
 1294    MethodDef doCall              // anonymous class's doCall() with code
 1295  
 1296    // ResolveExpr
 1297    Str:MethodVar enclosingLocals // locals in scope
 1298    Bool usesCvars                // does this guy use vars from outer scope
 1299  }
 1300  
 1301  **************************************************************************
 1302  ** ExprId
 1303  **************************************************************************
 1304  
 1305  **
 1306  ** ExprId uniquely identifies the type of expr
 1307  **
 1308  enum ExprId
 1309  {
 1310    nullLiteral,      // LiteralExpr
 1311    trueLiteral,
 1312    falseLiteral,
 1313    intLiteral,
 1314    floatLiteral,
 1315    strLiteral,
 1316    durationLiteral,
 1317    uriLiteral,
 1318    typeLiteral,
 1319    rangeLiteral,     // RangeLiteralExpr
 1320    listLiteral,      // ListLiteralExpr
 1321    mapLiteral,       // MapLiteralExpr
 1322    simpleLiteral,    // SimpleLiteralExpr
 1323    boolNot,          // UnaryExpr
 1324    cmpNull,
 1325    cmpNotNull,
 1326    assign,           // BinaryExpr
 1327    same,
 1328    notSame,
 1329    boolOr,           // CondExpr
 1330    boolAnd,
 1331    isExpr,           // TypeCheckExpr
 1332    asExpr,
 1333    cast,
 1334    call,             // CallExpr
 1335    shortcut,         // ShortcutExpr (has ShortcutOp)
 1336    field,            // FieldExpr
 1337    localVar,         // LocalVarExpr
 1338    thisExpr,         // ThisExpr
 1339    superExpr,        // SuperExpr
 1340    staticTarget,     // StaticTargetExpr
 1341    unknownVar,       // UnknownVarExpr
 1342    storage,
 1343    ternary,          // TernaryExpr
 1344    withBlock,        // WithBlockExpr
 1345    withBase,         // WithBaseExpr
 1346    curry,            // CurryExpr
 1347    closure           // ClosureExpr
 1348  }
 1349  
 1350  **************************************************************************
 1351  ** ShortcutId
 1352  **************************************************************************
 1353  
 1354  **
 1355  ** ShortcutOp is a sub-id for ExprId.shortcut which identifies the
 1356  ** an shortuct operation and it's method call
 1357  **
 1358  enum ShortcutOp
 1359  {
 1360    plus(2),
 1361    minus(2),
 1362    star(2),
 1363    slash(2),
 1364    percent(2),
 1365    lshift(2),
 1366    rshift(2),
 1367    amp(2),
 1368    pipe(2),
 1369    caret(2),
 1370    tilde(1),
 1371    negate(1),
 1372    increment(1),
 1373    decrement(1),
 1374    eq(2, "equals"),
 1375    cmp(2, "compare"),
 1376    get(2),
 1377    set(2),
 1378    slice(2)
 1379  
 1380    private new make(Int degree, Str methodName := null)
 1381    {
 1382      this.degree = degree
 1383      this.methodName = methodName == null ? name : methodName
 1384    }
 1385  
 1386    const Int degree
 1387    const Str methodName
 1388  }