logo
const class

compiler::SuppressedErr

sys::Obj
  sys::Err
    compiler::SuppressedErr
    1  //
    2  // Copyright (c) 2006, Brian Frank and Andy Frank
    3  // Licensed under the Academic Free License version 3.0
    4  //
    5  // History:
    6  //   15 Sep 05  Brian Frank  Creation
    7  //    6 Jun 06  Brian Frank  Ported from Java to Fan
    8  //
    9  
   10  **
   11  ** Parser is responsible for parsing a list of tokens into the
   12  ** abstract syntax tree.  At this point the CompilationUnit, Usings,
   13  ** and TypeDefs are already populated by the ScanForUsingAndTypes
   14  ** step.
   15  **
   16  public class Parser : CompilerSupport
   17  {
   18  
   19  //////////////////////////////////////////////////////////////////////////
   20  // Construction
   21  //////////////////////////////////////////////////////////////////////////
   22  
   23    **
   24    ** Construct the parser for the specified compilation unit.
   25    **
   26    new make(Compiler compiler, CompilationUnit unit, ClosureExpr[] closures)
   27      : super(compiler)
   28    {
   29      this.unit        = unit
   30      this.tokens      = unit.tokens
   31      this.numTokens   = unit.tokens.size
   32      this.closures    = closures
   33      this.suppressErr = false
   34      if (compiler != null) this.isSys = compiler.isSys
   35      reset(0)
   36    }
   37  
   38  //////////////////////////////////////////////////////////////////////////
   39  // Access
   40  //////////////////////////////////////////////////////////////////////////
   41  
   42    **
   43    ** Top level parse a compilation unit:
   44    **
   45    **   <compilationUnit>  :=  [<usings>] <typeDef>*
   46    **
   47    Void parse()
   48    {
   49      usings
   50      while (curt !== Token.eof) typeDef
   51    }
   52  
   53  //////////////////////////////////////////////////////////////////////////
   54  // Usings
   55  //////////////////////////////////////////////////////////////////////////
   56  
   57    **
   58    ** Parse <using>* - note that we are just skipping them because
   59    ** they are already parsed by ScanForUsingsAndTypes.
   60    **
   61    **   <using>     :=  <usingPod> | <usingType> | <usingAs>
   62    **   <usingPod>  :=  "using" <id> <eos>
   63    **   <usingType> :=  "using" <id> "::" <id> <eos>
   64    **   <usingAs>   :=  "using" <id> "::" <id> "as" <id> <eos>
   65    **
   66    private Void usings()
   67    {
   68      while (curt == Token.usingKeyword)
   69      {
   70        consume
   71        consumeId
   72        if (curt === Token.doubleColon)
   73        {
   74          consume
   75          consumeId
   76          if (curt === Token.asKeyword)
   77          {
   78            consume
   79            consumeId
   80          }
   81        }
   82        endOfStmt
   83      }
   84    }
   85  
   86  //////////////////////////////////////////////////////////////////////////
   87  // TypeDef
   88  //////////////////////////////////////////////////////////////////////////
   89  
   90    **
   91    ** TypeDef:
   92    **   <typeDef>      :=  <classDef> | <mixinDef> | <enumDef>
   93    **
   94    **   <classDef>     :=  <classHeader> <classBody>
   95    **   <classHeader>  :=  [<doc>] <facets> <typeFlags> "class" [<inheritance>]
   96    **   <classFlags>   :=  [<protection>] ["abstract"] ["final"]
   97    **   <classBody>    :=  "{" <slotDefs> "}"
   98    **
   99    **   <enumDef>      :=  <enumHeader> <enumBody>
  100    **   <enumHeader>   :=  [<doc>] <facets> <protection> "enum" [<inheritance>]
  101    **   <enumBody>     :=  "{" <enumDefs> <slotDefs> "}"
  102    **
  103    **   <mixinDef>     :=  <enumHeader> <enumBody>
  104    **   <mixinHeader>  :=  [<doc>] <facets> <protection> "mixin" [<inheritance>]
  105    **   <mixinBody>    :=  "{" <slotDefs> "}"
  106    **
  107    **   <protection>   :=  "public" | "protected" | "private" | "internal"
  108    **   <inheritance>  :=  ":" <typeList>
  109    **
  110    Void typeDef()
  111    {
  112      // [<doc>]
  113      doc := doc()
  114      if (curt === Token.eof) return
  115  
  116      // <facets>
  117      facets := facets()
  118  
  119      // <flags>
  120      flags := flags(false)
  121      if (flags & ~ProtectionMask == 0) flags |= FConst.Public
  122  
  123      // local working variables
  124      loc     := cur
  125      isMixin := false
  126      isEnum  := false
  127  
  128      // mixin, enum, or class
  129      if (curt === Token.mixinKeyword)
  130      {
  131        if (flags & FConst.Abstract != 0) err("The 'abstract' modifier is implied on mixin", loc)
  132        if (flags & FConst.Const != 0) err("Cannot use 'const' modifier on mixin", loc)
  133        if (flags & FConst.Final != 0) err("Cannot use 'final' modifier on mixin", loc)
  134        flags |= FConst.Mixin | FConst.Abstract
  135        isMixin = true
  136        consume
  137      }
  138      else if (curt === Token.enumKeyword)
  139      {
  140        if (flags & FConst.Const != 0) err("The 'const' modifier is implied on enum", loc)
  141        if (flags & FConst.Final != 0) err("The 'final' modifier is implied on enum", loc)
  142        if (flags & FConst.Abstract != 0) err("Cannot use 'abstract' modifier on enum", loc)
  143        flags |= FConst.Enum | FConst.Const | FConst.Final
  144        isEnum = true
  145        consume
  146      }
  147      else
  148      {
  149        consume(Token.classKeyword)
  150      }
  151  
  152      // name
  153      name := consumeId
  154      // lookup TypeDef
  155      def := unit.types.find |TypeDef def->Bool| { return def.name == name }
  156      if (def == null) throw err("Invalid class definition", cur)
  157  
  158      // populate it's doc, facets, and flags
  159      def.doc    = doc
  160      def.facets = facets
  161      def.flags  = flags
  162  
  163      // inheritance
  164      if (curt === Token.colon)
  165      {
  166        // first inheritance type can be extends or mixin
  167        consume
  168        first := typeRef
  169        if (!first.isMixin)
  170          def.base = first
  171        else
  172          def.mixins.add(first)
  173  
  174        // additional mixins
  175        while (curt === Token.comma)
  176        {
  177          consume
  178          def.mixins.add(typeRef)
  179        }
  180      }
  181  
  182      // if no inheritance specified then apply default base class
  183      if (def.base == null)
  184      {
  185        def.baseSpecified = false
  186        if (isEnum)
  187          def.base = ns.enumType
  188        else if (def.qname != "sys::Obj")
  189          def.base = ns.objType
  190      }
  191  
  192      // start class body
  193      consume(Token.lbrace)
  194  
  195      // if enum, parse values
  196      if (isEnum) enumDefs(def)
  197  
  198      // slots
  199      curType = def
  200      closureCount = 0
  201      while (true)
  202      {
  203        doc = this.doc
  204        if (curt === Token.rbrace) break
  205        slot := slotDef(def, doc)
  206  
  207        // do duplicate name error checking here
  208        if (def.hasSlotDef(slot.name))
  209        {
  210          err("Duplicate slot name '$slot.name'", slot.location)
  211        }
  212        else
  213        {
  214          def.addSlot(slot)
  215        }
  216      }
  217      closureCount = null
  218      curType = null
  219  
  220      // end of class body
  221      consume(Token.rbrace)
  222    }
  223  
  224    private Void mixins(TypeDef def)
  225    {
  226      consume  // extends or mixin
  227      def.mixins.add(typeRef)
  228      while (curt === Token.comma)
  229      {
  230        consume
  231        def.mixins.add(typeRef)
  232      }
  233    }
  234  
  235  //////////////////////////////////////////////////////////////////////////
  236  // Flags
  237  //////////////////////////////////////////////////////////////////////////
  238  
  239    **
  240    ** Parse any list of flags in any order, we will check invalid
  241    ** combinations in the CheckErrors step.
  242    **
  243    private Int flags(Bool normalize := true)
  244    {
  245      loc := cur
  246      flags := 0
  247      protection := false
  248      for (done := false; !done; )
  249      {
  250        oldFlags := flags
  251        switch (curt)
  252        {
  253          case Token.abstractKeyword:  flags |= FConst.Abstract
  254          case Token.constKeyword:     flags |= FConst.Const
  255          case Token.finalKeyword:     flags |= FConst.Final
  256          case Token.internalKeyword:  flags |= FConst.Internal;  protection = true
  257          case Token.nativeKeyword:    flags |= FConst.Native
  258          case Token.newKeyword:       flags |= FConst.Ctor
  259          case Token.overrideKeyword:  flags |= FConst.Override
  260          case Token.privateKeyword:   flags |= FConst.Private;   protection = true
  261          case Token.protectedKeyword: flags |= FConst.Protected; protection = true
  262          case Token.publicKeyword:    flags |= FConst.Public;    protection = true
  263          case Token.readonlyKeyword:  flags |= Readonly // Parser only flag
  264          case Token.staticKeyword:    flags |= FConst.Static
  265          case Token.virtualKeyword:   flags |= FConst.Virtual
  266          default:                     done = true
  267        }
  268        if (done) break
  269        if (oldFlags == flags) err("Repeated modifier")
  270        oldFlags = flags
  271        consume
  272      }
  273  
  274      if ((flags & FConst.Abstract !== 0) && (flags & FConst.Virtual !== 0))
  275        err("Abstract implies virtual", loc)
  276      if ((flags & FConst.Override !== 0) && (flags & FConst.Virtual !== 0))
  277        err("Override implies virtual", loc)
  278  
  279      if (normalize)
  280      {
  281        if (!protection) flags |= FConst.Public
  282        if (flags & FConst.Abstract !== 0) flags |= FConst.Virtual
  283        if (flags & FConst.Override !== 0)
  284        {
  285          if (flags & FConst.Final !== 0)
  286            flags &= ~FConst.Final
  287          else
  288            flags |= FConst.Virtual
  289        }
  290      }
  291  
  292      return flags
  293    }
  294  
  295  //////////////////////////////////////////////////////////////////////////
  296  // Enum
  297  //////////////////////////////////////////////////////////////////////////
  298  
  299    **
  300    ** Enum definition list:
  301    **   <enumDefs>  :=  <enumDef> ("," <enumDef>)* <eos>
  302    **
  303    private Void enumDefs(TypeDef def)
  304    {
  305      ordinal := 0
  306      def.enumDefs.add(enumDef(ordinal++))
  307      while (curt === Token.comma)
  308      {
  309        consume
  310        def.enumDefs.add(enumDef(ordinal++))
  311      }
  312      endOfStmt
  313    }
  314  
  315    **
  316    ** Enum definition:
  317    **   <enumDef>  :=  <id> ["(" <args> ")"]
  318    **
  319    private EnumDef enumDef(Int ordinal)
  320    {
  321      doc := doc()
  322  
  323      def := EnumDef.make(cur)
  324      def.doc = doc
  325      def.ordinal = ordinal
  326      def.name = consumeId
  327  
  328      // optional ctor args
  329      if (curt === Token.lparen)
  330      {
  331        consume(Token.lparen)
  332        if (curt != Token.rparen)
  333        {
  334          while (true)
  335          {
  336            def.ctorArgs.add( expr )
  337            if (curt === Token.rparen) break
  338            consume(Token.comma);
  339          }
  340        }
  341        consume(Token.rparen)
  342      }
  343  
  344      return def
  345    }
  346  
  347  //////////////////////////////////////////////////////////////////////////
  348  // Slots
  349  //////////////////////////////////////////////////////////////////////////
  350  
  351    **
  352    ** Slot definition:
  353    **   <slotDef> :=  <fieldDef> | <methodDef> | <ctorDef>
  354    **
  355    private SlotDef slotDef(TypeDef parent, Str[] doc)
  356    {
  357      // check for static {} class initialization
  358      if (curt === Token.staticKeyword && peekt === Token.lbrace)
  359      {
  360        location := cur
  361        consume
  362        curMethod = MethodDef.makeStaticInit(location, parent, null)
  363        curMethod.code = block(true)
  364        return curMethod
  365      }
  366  
  367      // all members start with facets, flags
  368      loc := cur
  369      facets := facets()
  370      flags := flags()
  371  
  372      // check if this is a Java style constructor, log error and parse like Fan sytle ctor
  373      if (curt === Token.identifier && cur.val == parent.name && peekt == Token.lparen)
  374      {
  375        err("Invalid constructor syntax - use new keyword")
  376        return methodDef(loc, parent, doc, facets, flags|FConst.Ctor, TypeRef.make(loc, ns.voidType), consumeId)
  377      }
  378  
  379      // check for inferred typed field
  380      // if = used rather than := then fieldDef() will log error
  381      if (curt === Token.identifier && (peekt === Token.defAssign || peekt === Token.assign))
  382      {
  383        name := consumeId
  384        return fieldDef(loc, parent, doc, facets, flags, null, name)
  385      }
  386  
  387      // check for constructor
  388      if (flags & FConst.Ctor !== 0)
  389      {
  390        name := consumeId
  391        return methodDef(loc, parent, doc, facets, flags, TypeRef.make(loc, ns.voidType), name)
  392      }
  393  
  394      // otherwise must be field or method
  395      type := typeRef
  396      name := consumeId
  397      if (curt === Token.lparen)
  398      {
  399        return methodDef(loc, parent, doc, facets, flags, type, name)
  400      }
  401      else
  402      {
  403        return fieldDef(loc, parent, doc, facets, flags, type, name)
  404      }
  405    }
  406  
  407  //////////////////////////////////////////////////////////////////////////
  408  // FieldDef
  409  //////////////////////////////////////////////////////////////////////////
  410  
  411    **
  412    ** Field definition:
  413    **   <fieldDef>     :=  <facets> <fieldFlags> [<type>] <id> [":=" <expr>]
  414    **                      [ "{" [<fieldGetter>] [<fieldSetter>] "}" ] <eos>
  415    **   <fieldFlags>   :=  [<protection>] ["readonly"] ["static"]
  416    **   <fieldGetter>  :=  "get" (<eos> | <block>)
  417    **   <fieldSetter>  :=  <protection> "set" (<eos> | <block>)
  418    **
  419    private FieldDef fieldDef(Location loc, TypeDef parent, Str[] doc, Str:FacetDef facets, Int flags, TypeRef type, Str name)
  420    {
  421      // define field itself
  422      field := FieldDef.make(loc, parent)
  423      field.doc       = doc
  424      field.facets    = facets
  425      field.flags     = flags & ~ParserFlagsMask
  426      field.fieldType = type
  427      field.name      = name
  428  
  429      // const always has storage, otherwise assume no storage
  430      // until proved otherwise in ResolveExpr step or we
  431      // auto-generate getters/setters
  432      if (field.isConst)
  433        field.flags |= FConst.Storage
  434  
  435      // field initializer
  436      if (curt === Token.defAssign || curt === Token.assign)
  437      {
  438        if (curt === Token.assign) err("Must use := for field initialization")
  439        consume
  440        inFieldInit = true
  441        field.init = expr
  442        inFieldInit = false
  443      }
  444  
  445      // disable type inference for now - doing inference for literals is
  446      // pretty trivial, but other types is tricky;  I'm not sure it is such
  447      // a hot idea anyways so it may just stay disabled forever
  448      if (type == null)
  449        err("Type inference not supported for fields", loc)
  450  
  451      // if not const, define getter/setter methods
  452      if (!field.isConst) defGetAndSet(field)
  453  
  454      // explicit getter or setter
  455      if (curt === Token.lbrace)
  456      {
  457        consume(Token.lbrace)
  458        getOrSet(field)
  459        getOrSet(field)
  460        consume(Token.rbrace)
  461      }
  462  
  463      // generate synthetic getter or setter code if necessary
  464      if (!field.isConst)
  465      {
  466        if (field.get.code == null) genSyntheticGet(field)
  467        if (field.set.code == null) genSyntheticSet(field)
  468      }
  469  
  470      // readonly is syntatic sugar for { private set }
  471      if (flags & Readonly !== 0)
  472      {
  473        field.set.flags = (field.set.flags & ProtectionMask) | FConst.Private
  474      }
  475  
  476      endOfStmt
  477      return field
  478    }
  479  
  480    private Void defGetAndSet(FieldDef f)
  481    {
  482      loc := f.location
  483  
  484      // getter MethodDef
  485      get := MethodDef.make(loc, f.parentDef)
  486      get.accessorFor = f
  487      get.flags = f.flags | FConst.Getter
  488      get.name  = f.name
  489      get.ret   = f.fieldType
  490      f.get = get
  491  
  492      // setter MethodDef
  493      set := MethodDef.make(loc, f.parentDef)
  494      set.accessorFor = f
  495      set.flags = f.flags | FConst.Setter
  496      set.name  = f.name
  497      set.ret   = ns.voidType
  498      set.params.add(ParamDef.make(loc, f.fieldType, "val"))
  499      f.set = set
  500    }
  501  
  502    private Void genSyntheticGet(FieldDef f)
  503    {
  504      loc := f.location
  505      f.get.flags |= FConst.Synthetic
  506      if (!f.isAbstract)
  507      {
  508        f.flags |= FConst.Storage
  509        f.get.code = Block.make(loc)
  510        f.get.code.add(ReturnStmt.make(loc, f.makeAccessorExpr(loc, false)))
  511      }
  512    }
  513  
  514    private Void genSyntheticSet(FieldDef f)
  515    {
  516      loc := f.location
  517      f.set.flags |= FConst.Synthetic
  518      if (!f.isAbstract)
  519      {
  520        f.flags |= FConst.Storage
  521        lhs := f.makeAccessorExpr(loc, false)
  522        rhs := UnknownVarExpr.make(loc, null"val")
  523        f.set.code = Block.make(loc)
  524        f.set.code.add(BinaryExpr.makeAssign(lhs, rhs).toStmt)
  525        f.set.code.add(ReturnStmt.make(loc))
  526      }
  527    }
  528  
  529    private Void getOrSet(FieldDef f)
  530    {
  531      loc := cur
  532      accessorFlags := flags(false)
  533      if (curt === Token.identifier)
  534      {
  535        // get or set
  536        idLoc := cur
  537        id := consumeId
  538  
  539        if (id == "get")
  540          curMethod = f.get
  541        else
  542          curMethod = f.set
  543  
  544        // { ...block... }
  545        Block block := null
  546        if (curt === Token.lbrace)
  547          block = this.block(id != "get")
  548        else
  549          endOfStmt
  550  
  551        // const field cannot have getter/setter
  552        if (f.isConst)
  553        {
  554          err("Const field '$f.name' cannot have ${id}ter", idLoc)
  555          return
  556        }
  557  
  558        // map to get or set on FieldDef
  559        if (id == "get")
  560        {
  561          if (accessorFlags != 0) err("Cannot use modifiers on field getter", loc)
  562          f.get.code  = block
  563        }
  564        else if (id.equals("set"))
  565        {
  566          if (accessorFlags != 0)
  567          {
  568            if (accessorFlags & ProtectionMask != 0)
  569              err("Cannot use modifiers on field setter except to narrow protection", loc)
  570            f.set.flags = (f.set.flags & ProtectionMask) | accessorFlags
  571          }
  572          f.set.code = block
  573        }
  574        else
  575        {
  576          err("Expected 'get' or 'set', not '$id'", idLoc)
  577        }
  578      }
  579    }
  580  
  581  //////////////////////////////////////////////////////////////////////////
  582  // MethodDef
  583  //////////////////////////////////////////////////////////////////////////
  584  
  585    **
  586    ** Method definition:
  587    **   <methodDef>      :=  <facets> <methodFlags> <type> <id> "(" <params> ")" <methodBody>
  588    **   <methodFlags>    :=  [<protection>] ["virtual"] ["override"] ["abstract"] ["static"]
  589    **   <params>         :=  [<param> ("," <param>)*]
  590    **   <param>          :=  <type> <id> [":=" <expr>]
  591    **   <methodBody>     :=  <eos> | ( "{" <stmts> "}" )
  592    **
  593    private MethodDef methodDef(Location loc, TypeDef parent, Str[] doc, Str:FacetDef facets, Int flags, TypeRef ret, Str name)
  594    {
  595      method := MethodDef.make(loc, parent)
  596      method.doc    = doc
  597      method.facets = facets
  598      method.flags  = flags
  599      method.ret    = ret
  600      method.name   = name
  601  
  602      // enter scope
  603      curMethod = method
  604  
  605      // parameters
  606      consume(Token.lparen)
  607      if (curt !== Token.rparen)
  608      {
  609        while (true)
  610        {
  611          method.params.add(paramDef)
  612          if (curt === Token.rparen) break
  613          consume(Token.comma)
  614        }
  615      }
  616      consume(Token.rparen)
  617  
  618      // if no body expected
  619      if (flags & FConst.Abstract !== 0 || flags & FConst.Native !== 0 || isSys)
  620      {
  621        if (curt === Token.lbrace)
  622        {
  623          err("Abstract and native methods cannot have method body")
  624          block(ret.isVoid)  // keep parsing
  625        }
  626        else
  627        {
  628          endOfStmt
  629        }
  630        return method
  631      }
  632  
  633      // ctor chain
  634      if ((flags & FConst.Ctor !== 0) && (curt === Token.colon))
  635        method.ctorChain = ctorChain(method);
  636  
  637      // body
  638      if (curt != Token.lbrace)
  639        err("Expecting method body")
  640      else
  641        method.code = block(ret.isVoid)
  642  
  643      // exit scope
  644      curMethod = null
  645  
  646      return method
  647    }
  648  
  649    private ParamDef paramDef()
  650    {
  651      param := ParamDef.make(cur)
  652      param.paramType = typeRef
  653      param.name = consumeId
  654      if (curt === Token.defAssign || curt === Token.assign)
  655      {
  656        if (curt === Token.assign) err("Must use := for parameter default");
  657        consume
  658        param.def = expr
  659      }
  660      return param
  661    }
  662  
  663    private CallExpr ctorChain(MethodDef method)
  664    {
  665      consume(Token.colon)
  666      loc := cur
  667  
  668      call := CallExpr.make(loc)
  669      call.isCtorChain = true
  670      switch (curt)
  671      {
  672        case Token.superKeyword: consume; call.target = SuperExpr.make(loc)
  673        case Token.thisKeyword:  consume; call.target = ThisExpr.make(loc)
  674        defaultthrow err("Expecting this or super for constructor chaining", loc);
  675      }
  676  
  677      // we can omit name if super
  678      if (call.target.id === ExprId.superExpr && curt != Token.dot)
  679      {
  680        call.name = method.name
  681      }
  682      else
  683      {
  684        consume(Token.dot)
  685        call.name = consumeId
  686      }
  687  
  688      // TODO: omit args if pass thru?
  689      callArgs(call)
  690      return call
  691    }
  692  
  693  //////////////////////////////////////////////////////////////////////////
  694  // Facets
  695  //////////////////////////////////////////////////////////////////////////
  696  
  697    private Str:FacetDef facets()
  698    {
  699      if (curt !== Token.at) return null
  700  
  701      facets := Str:FacetDef[:]
  702      while (curt === Token.at)
  703      {
  704        consume
  705        loc := cur
  706        name := consumeId
  707        Expr val
  708        if (curt === Token.assign)
  709        {
  710          consume()
  711          val = expr
  712        }
  713        else
  714        {
  715          val = LiteralExpr.make(loc, ExprId.trueLiteral, ns.boolType, true)
  716        }
  717        if (facets[name] != null) err("Duplicate facet '$name'", loc)
  718        facets[name] = FacetDef.make(loc, name, val)
  719      }
  720      return facets
  721    }
  722  
  723  //////////////////////////////////////////////////////////////////////////
  724  // Block
  725  //////////////////////////////////////////////////////////////////////////
  726  
  727    **
  728    ** Top level for blocks which must be surrounded by braces
  729    **
  730    private Block block(Bool inVoid)
  731    {
  732      this.inVoid = inVoid
  733      verify(Token.lbrace)
  734      return stmtOrBlock
  735    }
  736  
  737    **
  738    ** <block>  :=  <stmt> | ( "{" <stmts> "}" )
  739    ** <stmts>  :=  <stmt>*
  740    **
  741    private Block stmtOrBlock()
  742    {
  743      block := Block.make(cur)
  744  
  745      if (curt !== Token.lbrace)
  746      {
  747        block.stmts.add( stmt )
  748      }
  749      else
  750      {
  751        consume(Token.lbrace)
  752        while (curt != Token.rbrace)
  753          block.stmts.add( stmt )
  754        consume(Token.rbrace)
  755      }
  756  
  757      return block
  758    }
  759  
  760  //////////////////////////////////////////////////////////////////////////
  761  // Statements
  762  //////////////////////////////////////////////////////////////////////////
  763  
  764    **
  765    ** Statement:
  766    **   <stmt>  :=  <break> | <continue> | <for> | <if> | <return> | <switch> |
  767    **               <throw> | <while> | <try> | <exprStmt> | <localDef>
  768    **
  769    private Stmt stmt()
  770    {
  771      // check for statement keywords
  772      switch (curt)
  773      {
  774        case Token.breakKeyword:    return breakStmt
  775        case Token.continueKeyword: return continueStmt
  776        case Token.forKeyword:      return forStmt
  777        case Token.ifKeyword:       return ifStmt
  778        case Token.returnKeyword:   return returnStmt
  779        case Token.switchKeyword:   return switchStmt
  780        case Token.throwKeyword:    return throwStmt
  781        case Token.tryKeyword:      return tryStmt
  782        case Token.whileKeyword:    return whileStmt
  783      }
  784  
  785      // at this point we either have an expr or local var declaration
  786      return exprOrLocalDefStmt(true)
  787    }
  788  
  789    **
  790    ** Expression or local variable declaration:
  791    **   <exprStmt>  :=  <expr> <eos>
  792    **   <localDef>  :=  [<type>] <id> [":=" <expr>] <eos>
  793    **
  794    private Stmt exprOrLocalDefStmt(Bool isEndOfStmt)
  795    {
  796      // see if this statement begins with a type literal
  797      loc := cur
  798      mark := pos
  799      localType := tryType
  800  
  801      // type followed by identifier must be local variable declaration
  802      if (localType != null && curt === Token.identifier)
  803      {
  804        return localDefStmt(loc, localType, isEndOfStmt)
  805      }
  806      reset(mark)
  807  
  808      // identifier followed by def assign is inferred typed local var declaration
  809      if (curt === Token.identifier && peekt === Token.defAssign)
  810      {
  811        return localDefStmt(loc, null, isEndOfStmt)
  812      }
  813  
  814      // if current is an identifer, save for special error handling
  815      Str id := (curt === Token.identifier) ? (Str)cur.val : null
  816  
  817      // otherwise assume it's a stand alone expression statement
  818      stmt := ExprStmt.make(expr)
  819      if (!isEndOfStmt) return stmt
  820      if (endOfStmt(null)) return stmt
  821  
  822      // report error
  823      if (id != null && curt === Token.identifier && (peekt === Token.defAssign || peekt === Token.assign))
  824        throw err("Unknown type '$id' for local declaration", loc)
  825      else
  826        throw err("Expected expression statement", loc)
  827    }
  828  
  829    **
  830    ** Parse local variable declaration, the current token must be
  831    ** the identifier of the local variable.
  832    **
  833    private LocalDefStmt localDefStmt(Location loc, CType localType, Bool isEndOfStmt)
  834    {
  835      stmt := LocalDefStmt.make(loc)
  836      stmt.ctype = localType
  837      stmt.name  = consumeId
  838  
  839      if (curt === Token.defAssign || curt === Token.assign)
  840      {
  841        if (curt === Token.assign) err("Must use := for declaration assignments")
  842        consume
  843        stmt.init = expr
  844      }
  845  
  846      if (isEndOfStmt) endOfStmt
  847      return stmt
  848    }
  849  
  850    **
  851    ** If/else statement:
  852    **   <if>  :=  "if" "(" <expr> ")" <block> [ "else" <block> ]
  853    **
  854    private IfStmt ifStmt()
  855    {
  856      stmt := IfStmt.make(cur)
  857      consume(Token.ifKeyword)
  858      consume(Token.lparen)
  859      stmt.condition = expr
  860      consume(Token.rparen)
  861      stmt.trueBlock = stmtOrBlock
  862      if (curt === Token.elseKeyword)
  863      {
  864        consume(Token.elseKeyword)
  865        stmt.falseBlock = stmtOrBlock
  866      }
  867      return stmt
  868    }
  869  
  870    **
  871    ** Return statement:
  872    **   <return>  :=  "return" [<expr>] <eos>
  873    **
  874    private ReturnStmt returnStmt()
  875    {
  876      stmt := ReturnStmt.make(cur)
  877      consume(Token.returnKeyword)
  878      if (inVoid)
  879      {
  880        endOfStmt("Expected end of statement after return in Void method")
  881      }
  882      else
  883      {
  884        stmt.expr = expr
  885        endOfStmt
  886      }
  887      return stmt
  888    }
  889  
  890    **
  891    ** Throw statement:
  892    **   <throw>  :=  "throw" <expr> <eos>
  893    **
  894    private ThrowStmt throwStmt()
  895    {
  896      stmt := ThrowStmt.make(cur)
  897      consume(Token.throwKeyword)
  898      stmt.exception = expr
  899      endOfStmt
  900      return stmt
  901    }
  902  
  903    **
  904    ** While statement:
  905    **   <while>  :=  "while" "(" <expr> ")" <block>
  906    **
  907    private WhileStmt whileStmt()
  908    {
  909      stmt := WhileStmt.make(cur)
  910      consume(Token.whileKeyword)
  911      consume(Token.lparen)
  912      stmt.condition = expr
  913      consume(Token.rparen)
  914      stmt.block = stmtOrBlock
  915      return stmt
  916    }
  917  
  918    **
  919    ** For statement:
  920    **   <for>      :=  "for" "(" [<forInit>] ";" <expr> ";" <expr> ")" <block>
  921    **   <forInit>  :=  <expr> | <localDef>
  922    **
  923    private ForStmt forStmt()
  924    {
  925      stmt := ForStmt.make(cur)
  926      consume(Token.forKeyword)
  927      consume(Token.lparen)
  928  
  929      if (curt !== Token.semicolon) stmt.init = exprOrLocalDefStmt(false)
  930      consume(Token.semicolon)
  931  
  932      if (curt != Token.semicolon) stmt.condition = expr
  933      consume(Token.semicolon)
  934  
  935      if (curt != Token.rparen) stmt.update = expr
  936      consume(Token.rparen)
  937  
  938      stmt.block = stmtOrBlock
  939      return stmt
  940    }
  941  
  942    **
  943    ** Break statement:
  944    **   <break>  :=  "break" <eos>
  945    **
  946    private BreakStmt breakStmt()
  947    {
  948      stmt := BreakStmt.make(cur)
  949      consume(Token.breakKeyword)
  950      endOfStmt
  951      return stmt
  952    }
  953  
  954    **
  955    ** Continue statement:
  956    **   <continue>  :=  "continue" <eos>
  957    **
  958    private ContinueStmt continueStmt()
  959    {
  960      stmt := ContinueStmt.make(cur)
  961      consume(Token.continueKeyword)
  962      endOfStmt
  963      return stmt
  964    }
  965  
  966    **
  967    ** Try-catch-finally statement:
  968    **   <try>       :=  "try" "{" <stmt>* "}" <catch>* [<finally>]
  969    **   <catch>     :=  "catch" [<catchDef>] "{" <stmt>* "}"
  970    **   <catchDef>  :=  "(" <type> <id> ")"
  971    **   <finally>   :=  "finally" "{" <stmt>* "}"
  972    **
  973    private TryStmt tryStmt()
  974    {
  975      stmt := TryStmt.make(cur)
  976      consume(Token.tryKeyword)
  977      verify(Token.lbrace)
  978      stmt.block = block(inVoid)
  979      if (curt !== Token.catchKeyword && curt !== Token.finallyKeyword)
  980        throw err("Expecting catch or finally block")
  981      while (curt === Token.catchKeyword)
  982      {
  983        stmt.catches.add(tryCatch)
  984      }
  985      if (curt === Token.finallyKeyword)
  986      {
  987        consume
  988        verify(Token.lbrace)
  989        stmt.finallyBlock = block(inVoid)
  990      }
  991      return stmt
  992    }
  993  
  994    private Catch tryCatch()
  995    {
  996      c := Catch.make(cur)
  997      consume(Token.catchKeyword)
  998  
  999      if (curt === Token.lparen)
 1000      {
 1001        consume(Token.lparen)
 1002        c.errType = typeRef
 1003        c.errVariable = consumeId
 1004        consume(Token.rparen)
 1005      }
 1006  
 1007      c.block = block(inVoid)
 1008  
 1009      // insert implicit local variable declaration
 1010      if (c.errVariable != null)
 1011        c.block.stmts.insert(0, LocalDefStmt.makeCatchVar(c))
 1012  
 1013      return c
 1014    }
 1015  
 1016    **
 1017    ** Switch statement:
 1018    **   <switch>   :=  "switch" "(" <expr> ")" "{" <case>* [<default>] "}"
 1019    **   <case>     :=  "case" <expr> ":" <stmts>
 1020    **   <default>  :=  "default" ":" <stmts>
 1021    **
 1022    private SwitchStmt switchStmt()
 1023    {
 1024      stmt := SwitchStmt.make(cur)
 1025      consume(Token.switchKeyword)
 1026      consume(Token.lparen)
 1027      stmt.condition = expr
 1028      consume(Token.rparen)
 1029      consume(Token.lbrace)
 1030      while (curt != Token.rbrace)
 1031      {
 1032        if (curt === Token.caseKeyword)
 1033        {
 1034          c := Case.make(cur)
 1035          while (curt === Token.caseKeyword)
 1036          {
 1037            consume
 1038            c.cases.add(expr)
 1039            consume(Token.colon)
 1040          }
 1041          if (curt !== Token.defaultKeyword) // optimize away case fall-thru to default
 1042          {
 1043            c.block = switchBlock
 1044            stmt.cases.add(c)
 1045          }
 1046        }
 1047        else if (curt === Token.defaultKeyword)
 1048        {
 1049          if (stmt.defaultBlock != null) err("Duplicate default blocks")
 1050          consume
 1051          consume(Token.colon)
 1052          stmt.defaultBlock = switchBlock
 1053        }
 1054        else
 1055        {
 1056          throw err("Expected case or default statement")
 1057        }
 1058      }
 1059      consume(Token.rbrace)
 1060      endOfStmt
 1061      return stmt
 1062    }
 1063  
 1064    private Block switchBlock()
 1065    {
 1066      Block block := null
 1067      while (curt !== Token.caseKeyword && curt != Token.defaultKeyword && curt !== Token.rbrace)
 1068      {
 1069        if (block == null) block = Block.make(cur)
 1070        block.stmts.add(stmt)
 1071      }
 1072      return block;
 1073    }
 1074  
 1075  //////////////////////////////////////////////////////////////////////////
 1076  // Expr
 1077  //////////////////////////////////////////////////////////////////////////
 1078  
 1079    **
 1080    ** Expression:
 1081    **   <expr>  :=  <assignExpr>
 1082    **
 1083    private Expr expr()
 1084    {
 1085      return assignExpr
 1086    }
 1087  
 1088    **
 1089    ** Assignment expression:
 1090    **   <assignExpr>  :=  <condOrExpr> [<assignOp> <assignExpr>]
 1091    **   <assignOp>    :=  "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>="  | "&=" | "^=" | "|="
 1092    **
 1093    private Expr assignExpr(Expr expr := null)
 1094    {
 1095      // this is tree if built to the right (others to the left)
 1096      if (expr == null) expr = ternary
 1097      if (curt.isAssign)
 1098      {
 1099        if (curt === Token.assign)
 1100          return BinaryExpr.make(expr, consume.kind, assignExpr)
 1101        else
 1102          return ShortcutExpr.makeBinary(expr, consume.kind, assignExpr)
 1103      }
 1104      return expr
 1105    }
 1106  
 1107    **
 1108    ** Ternary expression:
 1109    **   <ternaryExpr> :=  <condOrExpr> "?" <condOrExpr> ":" <condOrExpr>
 1110    **
 1111    private Expr ternary()
 1112    {
 1113      expr := condOrExpr
 1114      if (curt === Token.question)
 1115      {
 1116        condition := expr
 1117        consume(Token.question);
 1118        trueExpr := condOrExpr
 1119        consume(Token.colon)
 1120        falseExpr := condOrExpr
 1121        expr = TernaryExpr.make(condition, trueExpr, falseExpr)
 1122      }
 1123      return expr
 1124    }
 1125  
 1126    **
 1127    ** Conditional or expression:
 1128    **   <condOrExpr>  :=  <condAndExpr>  ("||" <condAndExpr>)*
 1129    **
 1130    private Expr condOrExpr()
 1131    {
 1132      expr := condAndExpr
 1133      if (curt === Token.doublePipe)
 1134      {
 1135        cond := CondExpr.make(expr, cur.kind)
 1136        while (curt === Token.doublePipe)
 1137        {
 1138          consume
 1139          cond.operands.add(condAndExpr)
 1140        }
 1141        expr = cond
 1142      }
 1143      return expr
 1144    }
 1145  
 1146    **
 1147    ** Conditional and expression:
 1148    **   <condAndExpr>  :=  <equalityExpr> ("&&" <equalityExpr>)*
 1149    **
 1150    private Expr condAndExpr()
 1151    {
 1152      expr := equalityExpr
 1153      if (curt === Token.doubleAmp)
 1154      {
 1155        cond := CondExpr.make(expr, cur.kind)
 1156        while (curt === Token.doubleAmp)
 1157        {
 1158          consume
 1159          cond.operands.add(equalityExpr)
 1160        }
 1161        expr = cond
 1162      }
 1163      return expr
 1164    }
 1165  
 1166    **
 1167    ** Equality expression:
 1168    **   <equalityExpr>  :=  <relationalExpr> (("==" | "!=" | "===" | "!==") <relationalExpr>)*
 1169    **
 1170    private Expr equalityExpr()
 1171    {
 1172      expr := relationalExpr
 1173      while (curt === Token.eq   || curt === Token.notEq ||
 1174             curt === Token.same || curt === Token.notSame)
 1175      {
 1176        lhs := expr
 1177        tok := consume.kind
 1178        rhs := relationalExpr
 1179  
 1180        // optimize for null literal
 1181        if (lhs.id === ExprId.nullLiteral || rhs.id === ExprId.nullLiteral)
 1182        {
 1183          id := (tok === Token.eq || tok === Token.same) ? ExprId.cmpNull : ExprId.cmpNotNull
 1184          operand := (lhs.id === ExprId.nullLiteral) ? rhs : lhs
 1185          expr = UnaryExpr.make(lhs.location, id, tok, operand)
 1186        }
 1187        else
 1188        {
 1189          if (tok === Token.same || tok === Token.notSame)
 1190            expr = BinaryExpr.make(lhs, tok, rhs)
 1191          else
 1192            expr = ShortcutExpr.makeBinary(lhs, tok, rhs)
 1193        }
 1194      }
 1195      return expr
 1196    }
 1197  
 1198    **
 1199    ** Relational expression:
 1200    **   <relationalExpr> :=  <rangeExpr> (("is" | "as" | "<" | "<=" | ">" | ">=" | "<=>") <rangeExpr>)*
 1201    **
 1202    private Expr relationalExpr()
 1203    {
 1204      expr := rangeExpr
 1205      while (curt === Token.isKeyword || curt === Token.asKeyword ||
 1206             curt === Token.lt || curt === Token.ltEq ||
 1207             curt === Token.gt || curt === Token.gtEq ||
 1208             curt === Token.cmp)
 1209      {
 1210        if (curt === Token.isKeyword)
 1211        {
 1212          consume
 1213          expr = TypeCheckExpr.make(expr.location, ExprId.isExpr, expr, ctype)
 1214        }
 1215        else if (curt === Token.asKeyword)
 1216        {
 1217          consume
 1218          expr = TypeCheckExpr.make(expr.location, ExprId.asExpr, expr, ctype)
 1219        }
 1220        else
 1221        {
 1222          expr = ShortcutExpr.makeBinary(expr, consume.kind, rangeExpr)
 1223        }
 1224      }
 1225      return expr
 1226    }
 1227  
 1228    **
 1229    ** Range expression:
 1230    **   <rangeExpr>  :=  <bitOrExpr> ((".." | "...") <bitOrExpr>)*
 1231    **
 1232    private Expr rangeExpr()
 1233    {
 1234      expr := bitOrExpr
 1235      if (curt === Token.dotDot || curt === Token.dotDotDot)
 1236      {
 1237        range := RangeLiteralExpr.make(expr.location, ns.rangeType)
 1238        range.start     = expr
 1239        range.exclusive = consume.kind === Token.dotDotDot
 1240        range.end       = bitOrExpr
 1241        return range
 1242      }
 1243      return expr
 1244    }
 1245  
 1246    **
 1247    ** Bitwise or expression:
 1248    **   <bitOrExpr>  :=  <bitAndExpr> (("^" | "|") <bitAndExpr>)*
 1249    **
 1250    private Expr bitOrExpr()
 1251    {
 1252      expr := bitAndExpr
 1253      while (curt === Token.caret || curt === Token.pipe)
 1254        expr = ShortcutExpr.makeBinary(expr, consume.kind, bitAndExpr)
 1255      return expr
 1256    }
 1257  
 1258    **
 1259    ** Bitwise and expression:
 1260    **   <bitAndExpr>  :=  <shiftExpr> (("&" <shiftExpr>)*
 1261    **
 1262    private Expr bitAndExpr()
 1263    {
 1264      expr := shiftExpr
 1265      while (curt === Token.amp)
 1266        expr = ShortcutExpr.makeBinary(expr, consume.kind, shiftExpr)
 1267      return expr
 1268    }
 1269  
 1270    **
 1271    ** Bitwise shift expression:
 1272    **   <shiftExpr>  :=  <addExpr> (("<<" | ">>") <addExpr>)*
 1273    **
 1274    private Expr shiftExpr()
 1275    {
 1276      expr := additiveExpr
 1277      while (curt === Token.lshift || curt === Token.rshift)
 1278        expr = ShortcutExpr.makeBinary(expr, consume.kind, additiveExpr)
 1279      return expr
 1280    }
 1281  
 1282    **
 1283    ** Additive expression:
 1284    **   <addExpr>  :=  <multExpr> (("+" | "-") <multExpr>)*
 1285    **
 1286    private Expr additiveExpr()
 1287    {
 1288      expr := multiplicativeExpr
 1289      while (curt === Token.plus || curt === Token.minus)
 1290        expr = ShortcutExpr.makeBinary(expr, consume.kind, multiplicativeExpr)
 1291      return expr
 1292    }
 1293  
 1294    **
 1295    ** Multiplicative expression:
 1296    **   <multExpr>  :=  <parenExpr> (("*" | "/" | "%") <parenExpr>)*
 1297    **
 1298    private Expr multiplicativeExpr()
 1299    {
 1300      expr := parenExpr
 1301      while (curt === Token.star || curt === Token.slash || curt === Token.percent)
 1302        expr = ShortcutExpr.makeBinary(expr, consume.kind, parenExpr)
 1303      return expr
 1304    }
 1305  
 1306    **
 1307    ** Paren grouped expression:
 1308    **   <parenExpr>    :=  <unaryExpr> | <castExpr> | <groupedExpr>
 1309    **   <castExpr>     :=  "(" <type> ")" <parenExpr>
 1310    **   <groupedExpr>  :=  "(" <expr> ")" <termChain>*
 1311    **
 1312    private Expr parenExpr()
 1313    {
 1314      if (curt != Token.lparen)
 1315        return unaryExpr
 1316  
 1317      // consume opening paren
 1318      loc := cur
 1319      consume(Token.lparen)
 1320  
 1321      // In Fan just like C# and Java, a paren could mean
 1322      // either a cast or a parenthesized expression
 1323      mark := pos
 1324      castType := tryType
 1325      if (curt === Token.rparen)
 1326      {
 1327        consume
 1328        return TypeCheckExpr.make(loc, ExprId.cast, parenExpr, castType)
 1329      }
 1330      reset(mark)
 1331  
 1332      // this is just a normal parenthesized expression
 1333      expr := expr
 1334      consume(Token.rparen)
 1335      while (true)
 1336      {
 1337        chained := termChainExpr(expr)
 1338        if (chained == null) break
 1339        expr = chained
 1340      }
 1341      return expr
 1342    }
 1343  
 1344    **
 1345    ** Unary expression:
 1346    **   <unaryExpr>    :=  <prefixExpr> | <termExpr> | <postfixExpr>
 1347    **   <prefixExpr>   :=  ("!" | "+" | "-" | "~" | "++" | "--") <parenExpr>
 1348    **   <postfixExpr>  :=  <termExpr> ("++" | "--")
 1349    **
 1350    private Expr unaryExpr()
 1351    {
 1352      loc := cur
 1353      tok := cur
 1354      tokt := curt
 1355  
 1356      if (tokt === Token.bang)
 1357      {
 1358        consume
 1359        return UnaryExpr.make(loc, tokt.toExprId, tokt, parenExpr)
 1360      }
 1361  
 1362      if (tokt === Token.amp)
 1363      {
 1364        consume
 1365        return CurryExpr.make(loc, parenExpr)
 1366      }
 1367  
 1368      if (tokt === Token.plus)
 1369      {
 1370        consume
 1371        return parenExpr // optimize +expr to just expr
 1372      }
 1373  
 1374      if (tokt === Token.tilde || tokt === Token.minus)
 1375      {
 1376        consume
 1377        return ShortcutExpr.makeUnary(loc, tokt, parenExpr)
 1378      }
 1379  
 1380      if (tokt === Token.increment || tokt === Token.decrement)
 1381      {
 1382        consume
 1383        return ShortcutExpr.makeUnary(loc, tokt, parenExpr)
 1384      }
 1385  
 1386      expr := termExpr
 1387  
 1388      tokt = curt
 1389      if (tokt === Token.increment || tokt == Token.decrement)
 1390      {
 1391        consume
 1392        shortcut := ShortcutExpr.makeUnary(loc, tokt, expr)
 1393        shortcut.isPostfixLeave = true
 1394        return shortcut
 1395      }
 1396  
 1397      return expr
 1398    }
 1399  
 1400  //////////////////////////////////////////////////////////////////////////
 1401  // Term Expr
 1402  //////////////////////////////////////////////////////////////////////////
 1403  
 1404    **
 1405    ** A term is a base terminal such as a variable, call, or literal,
 1406    ** optionally followed by a chain of accessor expressions - such
 1407    ** as "x.y[z](a, b)".
 1408    **
 1409    **   <termExpr>  :=  <termBase> <termChain>* [withBlock]
 1410    **
 1411    private Expr termExpr(Expr target := null)
 1412    {
 1413      if (target == null) target = termBaseExpr
 1414      while (true)
 1415      {
 1416        chained := termChainExpr(target)
 1417        if (chained == null) break
 1418        target = chained
 1419      }
 1420      if (curt == Token.lbrace)
 1421        return withBlock(target)
 1422      return target
 1423    }
 1424  
 1425    **
 1426    ** Atomic base of a termExpr
 1427    **
 1428    **   <termBase>  :=  <literal> | <idExpr> | <closure>
 1429    **   <literal>   :=  "null" | "this" | "super" | <bool> | <int> |
 1430    **                   <float> | <str> | <duration> | <list> | <map> | <uri>
 1431    **
 1432    private Expr termBaseExpr()
 1433    {
 1434      loc := cur
 1435  
 1436      ctype := tryType
 1437      if (ctype != null) return typeBaseExpr(loc, ctype)
 1438  
 1439      switch (curt)
 1440      {
 1441        case Token.at:              return idExpr(nullfalse)
 1442        case Token.identifier:      return idExpr(nullfalse)
 1443        case Token.intLiteral:      return LiteralExpr.make(loc, ExprId.intLiteral, ns.intType, consume.val)
 1444        case Token.floatLiteral:    return LiteralExpr.make(loc, ExprId.floatLiteral, ns.floatType, consume.val)
 1445        case Token.strLiteral:      return LiteralExpr.make(loc, ExprId.strLiteral, ns.strType, consume.val)
 1446        case Token.durationLiteral: return LiteralExpr.make(loc, ExprId.durationLiteral, ns.durationType, consume.val)
 1447        case Token.uriLiteral:      return LiteralExpr.make(loc, ExprId.uriLiteral, ns.uriType, consume.val)
 1448        case Token.lbracket:        return collectionLiteralExpr(loc, null)
 1449        case Token.falseKeyword:    consume; return LiteralExpr.make(loc, ExprId.falseLiteral, ns.boolType, false)
 1450        case Token.nullKeyword:     consume; return LiteralExpr.make(loc, ExprId.nullLiteral, ns.objType, null)
 1451        case Token.superKeyword:    consume; return SuperExpr.make(loc)
 1452        case Token.thisKeyword:     consume; return ThisExpr.make(loc)
 1453        case Token.trueKeyword:     consume; return LiteralExpr.make(loc, ExprId.trueLiteral, ns.boolType, true)
 1454      }
 1455      throw err("Expected expression, not '" + cur + "'")
 1456    }
 1457  
 1458    **
 1459    ** Handle a term expression which begins with a type literal.
 1460    **
 1461    private Expr typeBaseExpr(Location loc, CType ctype)
 1462    {
 1463      // type literal
 1464      if (curt === Token.dot && peekt === Token.identifier && peek.val == "type")
 1465      {
 1466        consume
 1467        consume
 1468        return LiteralExpr.make(loc, ExprId.typeLiteral, ns.typeType, ctype)
 1469      }
 1470  
 1471      // dot is named super or static call chain
 1472      if (curt == Token.dot)
 1473      {
 1474        consume
 1475        if (curt === Token.superKeyword)
 1476        {
 1477          consume
 1478          return SuperExpr.make(loc, ctype)
 1479        }
 1480        else
 1481        {
 1482          return idExpr(StaticTargetExpr.make(loc, ctype)false)
 1483        }
 1484      }
 1485  
 1486      // list/map literal with explicit type
 1487      if (curt === Token.lbracket)
 1488      {
 1489        return collectionLiteralExpr(loc, ctype)
 1490      }
 1491  
 1492      // closure
 1493      if (curt == Token.lbrace && ctype is FuncType)
 1494      {
 1495        return closure(loc, (FuncType)ctype)
 1496      }
 1497  
 1498      // simple literal type(arg)
 1499      if (curt == Token.lparen)
 1500      {
 1501        consume
 1502        arg := expr
 1503        consume(Token.rparen)
 1504        return SimpleLiteralExpr.make(loc, ctype, arg)
 1505      }
 1506  
 1507      // complex literal type {...}
 1508      if (curt == Token.lbrace)
 1509      {
 1510        base := UnknownVarExpr.make(loc, StaticTargetExpr.make(loc, ctype)"make")
 1511        return withBlock(base)
 1512      }
 1513  
 1514      throw err("Unexpected type literal", loc)
 1515    }
 1516  
 1517    **
 1518    ** A chain expression is a piece of a term expression that may
 1519    ** be chained together such as "call.var[x]".  If the specified
 1520    ** target expression contains a chained access, then return the new
 1521    ** expression, otherwise return null.
 1522    **
 1523    **   <termChain>      :=  <compiledCall> | <dynamicCall> | <indexExpr>
 1524    **   <compiledCall>   :=  "." <idExpr>
 1525    **   <dynamicCall>    :=  "->" <idExpr>
 1526    **
 1527    private Expr termChainExpr(Expr target)
 1528    {
 1529      loc := cur
 1530  
 1531      // if ".id" field access or ".id" call
 1532      if (curt === Token.dot)
 1533      {
 1534        consume
 1535        return idExpr(target, false)
 1536      }
 1537  
 1538      // if "->id" dynamic call
 1539      if (curt === Token.arrow)
 1540      {
 1541        consume
 1542        return idExpr(target, true)
 1543      }
 1544  
 1545      // if target[...]
 1546      if (curt === Token.lbracket)
 1547        return indexExpr(target)
 1548  
 1549      // if target(...)
 1550      if (curt === Token.lparen)
 1551        return callOp(target)
 1552  
 1553      // we treat a with base as a dot slot access
 1554      if (target.id === ExprId.withBase)
 1555        return idExpr(target, false)
 1556  
 1557      // otherwise the expression should be finished
 1558      return null;
 1559    }
 1560  
 1561    **
 1562    ** A with block is a series of sub-expressions
 1563    ** inside {} appended to the end of an expression.
 1564    **
 1565    private Expr withBlock(Expr base)
 1566    {
 1567      // field initializers look like a with block, but
 1568      // we can safely peek to see if the next token is "get",
 1569      // "set", or a keyword like "private"
 1570      if (inFieldInit)
 1571      {
 1572        if (peek.kind.keyword) return base
 1573        if (peekt == Token.identifier)
 1574        {
 1575          if (peek.val == "get" || peek.val == "set") return base
 1576        }
 1577      }
 1578  
 1579      withBlock := WithBlockExpr.make(base)
 1580      consume(Token.lbrace)
 1581      while (curt !== Token.rbrace)
 1582      {
 1583        sub  := assignExpr(termExpr(WithBaseExpr.make(withBlock)))
 1584        withBlock.subs.add(sub)
 1585        endOfStmt
 1586      }
 1587      consume(Token.rbrace)
 1588      return withBlock
 1589    }
 1590  
 1591  //////////////////////////////////////////////////////////////////////////
 1592  // Term Expr Utils
 1593  //////////////////////////////////////////////////////////////////////////
 1594  
 1595    **
 1596    ** Identifier expression:
 1597    **   <idExpr>  :=  <local> | <field> | <call>
 1598    **   <local>   :=  <id>
 1599    **   <field>   :=  ["@"] <id>
 1600    **
 1601    private Expr idExpr(Expr target, Bool dynamicCall)
 1602    {
 1603      loc := cur
 1604  
 1605      if (curt == Token.at)
 1606      {
 1607        consume
 1608        return UnknownVarExpr.makeStorage(loc, target, consumeId)
 1609      }
 1610  
 1611      if (peekt === Token.lparen)
 1612      {
 1613        call := callExpr(target)
 1614        call.isDynamic = dynamicCall
 1615        return call
 1616      }
 1617  
 1618      name := consumeId
 1619  
 1620      // if we have a closure then this is a call with one arg of a closure
 1621      closure := tryClosure
 1622      if (closure != null)
 1623      {
 1624        call := CallExpr.make(loc)
 1625        call.target = target
 1626        call.name   = name
 1627        call.args.add(closure)
 1628        return call
 1629      }
 1630  
 1631      // if dynamic call then we know this is a call not a field
 1632      if (dynamicCall)
 1633      {
 1634        call := CallExpr.make(loc)
 1635        call.target    = target
 1636        call.name      = name
 1637        call.isDynamic = true
 1638        return call
 1639      }
 1640  
 1641      return UnknownVarExpr.make(loc, target, name)
 1642    }
 1643  
 1644    **
 1645    ** Call expression:
 1646    **   <call>  :=  <id> ["(" <args> ")"] [<closure>]
 1647    **
 1648    private CallExpr callExpr(Expr target)
 1649    {
 1650      call := CallExpr.make(cur)
 1651      call.target  = target
 1652      call.name    = consumeId
 1653      callArgs(call)
 1654      return call
 1655    }
 1656  
 1657    **
 1658    ** Parse args with known parens:
 1659    **   <args>  := [<expr> ("," <expr>)*] [<closure>]
 1660    **
 1661    private Void callArgs(CallExpr call)
 1662    {
 1663      consume(Token.lparen)
 1664      if (curt != Token.rparen)
 1665      {
 1666        while (true)
 1667        {
 1668          call.args.add(expr)
 1669          if (curt === Token.rparen) break
 1670          consume(Token.comma)
 1671        }
 1672      }
 1673      consume(Token.rparen)
 1674  
 1675      closure := tryClosure
 1676      if (closure != null) call.args.add(closure);
 1677    }
 1678  
 1679    **
 1680    ** Call operator:
 1681    **   <callOp>  := "(" <args> ")" [<closure>]
 1682    **
 1683    private Expr callOp(Expr target)
 1684    {
 1685      loc := cur
 1686      call := CallExpr.make(loc)
 1687      call.target = target
 1688      callArgs(call)
 1689      call.name = "call${call.args.size}"
 1690      return call
 1691    }
 1692  
 1693    **
 1694    ** Index expression:
 1695    **   <indexExpr>  := "[" <expr> "]"
 1696    **
 1697    private Expr indexExpr(Expr target)
 1698    {
 1699      loc := cur
 1700      consume(Token.lbracket)
 1701  
 1702      // otherwise this must be a standard single key index
 1703      expr := expr
 1704      consume(Token.rbracket)
 1705      return ShortcutExpr.makeGet(loc, target, expr)
 1706    }
 1707  
 1708  //////////////////////////////////////////////////////////////////////////
 1709  // Collection "Literals"
 1710  //////////////////////////////////////////////////////////////////////////
 1711  
 1712    **
 1713    ** Collection literal:
 1714    **   <list>       :=  [<type>] "[" <listItems> "]"
 1715    **   <listItems>  :=  "," | (<expr> ("," <expr>)*)
 1716    **   <map>        :=  [<mapType>] "[" <mapItems> "]"
 1717    **   <mapItems>   :=  ":" | (<mapPair> ("," <mapPair>)*)
 1718    **   <mapPair>    :=  <expr> ":" <expr>
 1719    **
 1720    private Expr collectionLiteralExpr(Location loc, CType explicitType)
 1721    {
 1722      // empty list [,]
 1723      if (peekt === Token.comma)
 1724        return listLiteralExpr(loc, explicitType, null)
 1725  
 1726      // empty map [:]
 1727      if (peekt === Token.colon)
 1728        return mapLiteralExpr(loc, explicitType, null)
 1729  
 1730      // opening bracket
 1731      consume(Token.lbracket)
 1732  
 1733      // [] is error
 1734      if (curt === Token.rbracket)
 1735      {
 1736        err("Invalid list literal; use '[,]' for empty Obj[] list", loc)
 1737        consume
 1738        return ListLiteralExpr.make(loc)
 1739      }
 1740  
 1741      // read first expression
 1742      first := expr
 1743  
 1744      // at this point we can determine if it is a list or a map
 1745      if (curt === Token.colon)
 1746        return mapLiteralExpr(loc, explicitType, first)
 1747      else
 1748        return listLiteralExpr(loc, explicitType, first)
 1749    }
 1750  
 1751    **
 1752    ** Parse List literal; if first is null then
 1753    **   cur must be on lbracket
 1754    ** else
 1755    **   cur must be on comma after first item
 1756    **
 1757    private ListLiteralExpr listLiteralExpr(Location loc, CType explicitType, Expr first)
 1758    {
 1759      // explicitType is type of List:  Str[,]
 1760      if (explicitType != null)
 1761        explicitType = explicitType.toListOf
 1762  
 1763      list := ListLiteralExpr.make(loc, (ListType)explicitType)
 1764  
 1765      // if first is null, must be on lbracket
 1766      if (first == null)
 1767      {
 1768        consume(Token.lbracket)
 1769  
 1770        // if [,] empty list
 1771        if (curt === Token.comma)
 1772        {
 1773          consume
 1774          consume(Token.rbracket)
 1775          return list
 1776        }
 1777  
 1778        first = expr
 1779      }
 1780  
 1781      list.vals.add(first)
 1782      while (curt === Token.comma)
 1783      {
 1784        consume
 1785        if (curt === Token.rbracket) break // allow extra trailing comma
 1786        list.vals.add(expr)
 1787      }
 1788      consume(Token.rbracket)
 1789      return list
 1790    }
 1791  
 1792    **
 1793    ** Parse Map literal; if first is null:
 1794    **   cur must be on lbracket
 1795    ** else
 1796    **   cur must be on colon of first key/value pair
 1797    **
 1798    private MapLiteralExpr mapLiteralExpr(Location loc, CType explicitType, Expr first)
 1799    {
 1800      // explicitType is *the* map type: Str:Str[,]
 1801      if (explicitType != null && !(explicitType is MapType))
 1802      {
 1803        err("Invalid map type '$explicitType' for map literal", loc)
 1804        explicitType = null
 1805      }
 1806  
 1807      map := MapLiteralExpr.make(loc, (MapType)explicitType)
 1808  
 1809      // if first is null, must be on lbracket
 1810      if (first == null)
 1811      {
 1812        consume(Token.lbracket)
 1813  
 1814        // if [,] empty list
 1815        if (curt === Token.colon)
 1816        {
 1817          consume
 1818          consume(Token.rbracket)
 1819          return map
 1820        }
 1821  
 1822        first = expr
 1823      }
 1824  
 1825      map.keys.add(first)
 1826      consume(Token.colon)
 1827      map.vals.add(expr)
 1828      while (curt === Token.comma)
 1829      {
 1830        consume
 1831        if (curt === Token.rbracket) break // allow extra trailing comma
 1832        map.keys.add(expr)
 1833        consume(Token.colon)
 1834        map.vals.add(expr)
 1835      }
 1836      consume(Token.rbracket)
 1837      return map
 1838    }
 1839  
 1840  //////////////////////////////////////////////////////////////////////////
 1841  // Closure
 1842  //////////////////////////////////////////////////////////////////////////
 1843  
 1844    **
 1845    ** Attempt to parse a closure expression or return null if we
 1846    ** aren't positioned at the start of a closure expression.
 1847    **
 1848    private ClosureExpr tryClosure()
 1849    {
 1850      loc := cur
 1851  
 1852      // if no pipe, then no closure
 1853      if (curt !== Token.pipe) return null
 1854  
 1855      // otherwise this can only be a FuncType declaration,
 1856      // so give it a whirl, and bail if that fails
 1857      mark := pos
 1858      funcType := (FuncType)tryType
 1859      if (funcType == null) return null
 1860  
 1861      // if we don't see opening brace for body - no go
 1862      if (curt !== Token.lbrace) { reset(mark)return null }
 1863  
 1864      return closure(loc, funcType)
 1865    }
 1866  
 1867    **
 1868    ** Parse body of closure expression and return ClosureExpr.
 1869    **
 1870    private ClosureExpr closure(Location loc, FuncType funcType)
 1871    {
 1872      if (curMethod == null)
 1873        throw err("Unexpected closure outside of a method")
 1874  
 1875      // closure anonymous class name: class$method$count
 1876      name := "${curType.name}\$${curMethod.name}\$${closureCount++}"
 1877  
 1878      // create closure
 1879      closure := ClosureExpr.make(loc, curType, curMethod, curClosure, funcType, name)
 1880  
 1881      // save all closures in global list and list per type
 1882      closures.add(closure)
 1883      curType.closures.add(closure)
 1884  
 1885      // parse block; temporarily change our inVoid flag and curClosure
 1886      oldInVoid := inVoid
 1887      oldClosure := curClosure
 1888      curClosure = closure
 1889      closure.code = block(funcType.ret.isVoid)
 1890      curClosure = oldClosure
 1891      inVoid = oldInVoid
 1892  
 1893      return closure
 1894    }
 1895  
 1896  //////////////////////////////////////////////////////////////////////////
 1897  // Types
 1898  //////////////////////////////////////////////////////////////////////////
 1899  
 1900    **
 1901    ** Parse a type production into a CType and wrap it as AST TypeRef.
 1902    **
 1903    private TypeRef typeRef()
 1904    {
 1905      Location loc := cur
 1906      return TypeRef.make(loc, ctype)
 1907    }
 1908  
 1909    **
 1910    ** If the current stream of tokens can be parsed as a
 1911    ** valid type production return it.  Otherwise leave
 1912    ** the parser positioned on the current token.
 1913    **
 1914    private CType tryType()
 1915    {
 1916      // types can only begin with identifier, | or [
 1917      if (curt !== Token.identifier && curt !== Token.pipe && curt !== Token.lbracket)
 1918        return null
 1919  
 1920      suppressErr = true
 1921      mark := pos
 1922      CType type := null
 1923      try
 1924      {
 1925        type = ctype
 1926      }
 1927      catch (SuppressedErr e)
 1928      {
 1929      }
 1930      suppressErr = false
 1931      if (type == null) reset(mark)
 1932      return type
 1933    }
 1934  
 1935    **
 1936    ** Type signature:
 1937    **   <type>      :=  <simpleType> | <listType> | <mapType> | <funcType>
 1938    **   <listType>  :=  <type> "[]"
 1939    **   <mapType>   :=  ["["] <type> ":" <type> ["]"]
 1940    **
 1941    private CType ctype()
 1942    {
 1943      CType t := null
 1944  
 1945      // Types can begin with:
 1946      //   - id
 1947      //   - [k:v]
 1948      //   - |a, b -> r|
 1949      if (curt === Token.identifier)
 1950      {
 1951        t = simpleType
 1952      }
 1953      else if (curt === Token.lbracket)
 1954      {
 1955        loc := consume(Token.lbracket)
 1956        t = ctype
 1957        consume(Token.rbracket)
 1958        if (!(is MapType)) err("Invalid map type", loc)
 1959      }
 1960      else if (curt === Token.pipe)
 1961      {
 1962        t = funcType
 1963      }
 1964      else
 1965      {
 1966        throw err("Expecting type name")
 1967      }
 1968  
 1969      // trailing [] for lists
 1970      while (curt === Token.lbracket && peekt === Token.rbracket)
 1971      {
 1972        consume(Token.lbracket)
 1973        consume(Token.rbracket)
 1974        t = t.toListOf
 1975      }
 1976  
 1977      // check for ":" for map type
 1978      if (curt === Token.colon)
 1979      {
 1980        consume(Token.colon)
 1981        key := t
 1982        val := ctype
 1983        t = MapType.make(key, val)
 1984      }
 1985  
 1986      return t
 1987    }
 1988  
 1989    **
 1990    ** Simple type signature:
 1991    **   <simpleType>  :=  <id> ["::" <id>]
 1992    **
 1993    private CType simpleType()
 1994    {
 1995      loc := cur
 1996      id := consumeId
 1997  
 1998      // fully qualified
 1999      if (curt === Token.doubleColon)
 2000      {
 2001        consume
 2002        return ResolveImports.resolveQualified(this, id, consumeId, loc)
 2003      }
 2004  
 2005      // unqualified name, lookup in imported types
 2006      types := unit.importedTypes[id]
 2007      if (types == null)
 2008      {
 2009        // handle sys generic parameters
 2010        if (isSys && id.size == 1)
 2011          return ns.genericParameter(id)
 2012  
 2013        // not found in imports
 2014        err("Unknown type '$id'", loc)
 2015        return ns.voidType
 2016      }
 2017  
 2018      // if more then one it is ambiguous
 2019      if (types.size > 1) err("Ambiguous type: " + types.join(", "))
 2020  
 2021      // got it
 2022      return types.first
 2023    }
 2024  
 2025    **
 2026    ** Method type signature:
 2027    **   <funcType>  :=  "|" <formals> ["->" <type> "|"
 2028    **   <formals>   :=  [<formal> ("," <formal>)*]
 2029    **   <formal>    :=  <type> <id>
 2030    **
 2031    private CType funcType()
 2032    {
 2033      params := CType[,]
 2034      names  := Str[,]
 2035      ret := ns.voidType
 2036  
 2037      // opening pipe
 2038      consume(Token.pipe)
 2039  
 2040      // |,| is the empty method type
 2041      if (curt === Token.comma)
 2042      {
 2043        consume
 2044        consume(Token.pipe)
 2045        return FuncType.make(params, names, ret)
 2046      }
 2047  
 2048      // params, must be one if no ->
 2049      if (curt !== Token.arrow)
 2050      {
 2051        params.add(ctype)
 2052        names.add(consumeId)
 2053      }
 2054      while (curt === Token.comma)
 2055      {
 2056        consume
 2057        params.add(ctype)
 2058        names.add(consumeId)
 2059      }
 2060  
 2061      // optional arrow
 2062      if (curt === Token.arrow)
 2063      {
 2064        consume
 2065        ret = ctype
 2066      }
 2067  
 2068      // closing pipe
 2069      consume(Token.pipe)
 2070      return FuncType.make(params, names, ret)
 2071    }
 2072  
 2073  //////////////////////////////////////////////////////////////////////////
 2074  // Misc
 2075  //////////////////////////////////////////////////////////////////////////
 2076  
 2077    **
 2078    ** Parse fandoc or retur null
 2079    **
 2080    private Str[] doc()
 2081    {
 2082      Str[] doc := null
 2083      while (curt === Token.docComment)
 2084        doc = (Str[])consume(Token.docComment).val
 2085      return doc
 2086    }
 2087  
 2088  //////////////////////////////////////////////////////////////////////////
 2089  // Errors
 2090  //////////////////////////////////////////////////////////////////////////
 2091  
 2092    override CompilerErr err(Str msg, Location loc := null)
 2093    {
 2094      if (suppressErr) throw SuppressedErr.make
 2095      if (loc == null) loc = cur
 2096      return super.err(msg, loc)
 2097    }
 2098  
 2099  //////////////////////////////////////////////////////////////////////////
 2100  // Tokens
 2101  //////////////////////////////////////////////////////////////////////////
 2102  
 2103    **
 2104    ** Verify current is an identifier, consume it, and return it.
 2105    **
 2106    private Str consumeId()
 2107    {
 2108      if (curt !== Token.identifier)
 2109        throw err("Expected identifier, not '$cur'");
 2110      return (Str)consume.val;
 2111    }
 2112  
 2113    **
 2114    ** Check that the current token matches the specified
 2115    ** type, but do not consume it.
 2116    **
 2117    private Void verify(Token kind)
 2118    {
 2119      if (curt !== kind)
 2120        throw err("Expected '$kind.symbol', not '$cur'");
 2121    }
 2122  
 2123    **
 2124    ** Consume the current token and return consumed token.
 2125    ** If kind is non-null then verify first
 2126    **
 2127    private TokenVal consume(Token kind := null)
 2128    {
 2129      // verify if not null
 2130      if (kind != null) verify(kind)
 2131  
 2132      // save the current we are about to consume for return
 2133      result := cur
 2134  
 2135      // get the next token from the buffer, if pos is past numTokens,
 2136      // then always use the last token which will be eof
 2137      TokenVal next;
 2138      pos++;
 2139      if (pos+1 < numTokens)
 2140        next = tokens[pos+1]  // next peek is cur+1
 2141      else
 2142        next = tokens[numTokens-1]
 2143  
 2144      this.cur   = peek
 2145      this.peek  = next
 2146      this.curt  = cur.kind
 2147      this.peekt = peek.kind
 2148  
 2149      return result
 2150    }
 2151  
 2152    **
 2153    ** Statements can be terminated with a semicolon, end of line
 2154    ** or } end of block.   Return true on success.  On failure
 2155    ** return false if errMsg is null or log/throw an exception.
 2156    **
 2157    private Bool endOfStmt(Str errMsg := "Expected end of statement: semicolon, newline, or end of block; not '$cur'")
 2158    {
 2159      if (cur.newline) return true
 2160      if (curt === Token.semicolon) { consume; return true }
 2161      if (curt === Token.rbrace) return true
 2162      if (errMsg == null) return false
 2163      throw err(errMsg)
 2164    }
 2165  
 2166    **
 2167    ** Reset the current position to the specified tokens index.
 2168    **
 2169    private Void reset(Int pos)
 2170    {
 2171      this.pos   = pos
 2172      this.cur   = tokens[pos]
 2173      if (pos+1 < numTokens)
 2174        this.peek  = tokens[pos+1]
 2175      else
 2176        this.peek  = tokens[pos]
 2177      this.curt  = cur.kind
 2178      this.peekt = peek.kind
 2179    }
 2180  
 2181  //////////////////////////////////////////////////////////////////////////
 2182  // Parser Flags
 2183  //////////////////////////////////////////////////////////////////////////
 2184  
 2185    // These are flags used only by the parser we merge with FConst
 2186    // flags by starting from most significant bit and working down
 2187    const static Int Readonly := 0x80000000
 2188    const static Int ParserFlagsMask := Readonly
 2189  
 2190    // Bitwise and this mask to clear all protection scope flags
 2191    const static Int ProtectionMask := ~(FConst.Public|FConst.Protected|FConst.Private|FConst.Internal)
 2192  
 2193  //////////////////////////////////////////////////////////////////////////
 2194  // Fields
 2195  //////////////////////////////////////////////////////////////////////////
 2196  
 2197    private CompilationUnit unit   // compilation unit to generate
 2198    private TokenVal[] tokens      // tokens all read in
 2199    private Int numTokens          // number of tokens
 2200    private Int pos;               // offset Into tokens for cur
 2201    private TokenVal cur           // current token
 2202    private Token curt             // current token type
 2203    private TokenVal peek          // next token
 2204    private Token peekt            // next token type
 2205    private Bool suppressErr       // throw SuppressedErr instead of CompilerErr
 2206    private Bool isSys := false    // are we parsing the sys pod itself
 2207    private Bool inVoid            // are we currently in a void method
 2208    private Bool inFieldInit := false // are we currently in a field initializer
 2209    private TypeDef curType        // current TypeDef scope
 2210    private MethodDef curMethod    // current MethodDef scope
 2211    private ClosureExpr curClosure // current ClosureExpr if inside closure
 2212    private Int closureCount       // number of closures parsed inside curMethod
 2213    private ClosureExpr[] closures // list of all closures parsed
 2214  
 2215  }
 2216  
 2217  //////////////////////////////////////////////////////////////////////////
 2218  // SuppressedErr
 2219  //////////////////////////////////////////////////////////////////////////
 2220  
 2221  const class SuppressedErr : Err
 2222  {
 2223    new make() : super(nullnull) {}
 2224  }

More Info

Slots