logo

class

compiler::CondExpr

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