code: private ForeachStatement parseForeachStatement(Token token)
          throws IOException {
  
          SourceInfo info = token.getSourceInfo();
  
          token = peek();
          if (token.getID() == Token.LPAREN) {
              read();
          }
          else {
              error("foreach.lparen.expected", token);
          }
  
          VariableRef loopVar = parseLValue();
  
          // mod for declarative typing
          boolean foundASToken = false;
          Token asToken = peek();
          if (asToken.getID() == Token.AS) {
              foundASToken = true;
              read();
              TypeName typeName = parseTypeName();
              SourceInfo info2 = peek().getSourceInfo();
              loopVar.setVariable(new Variable(info2, loopVar.getName(), typeName, true));
          }
          // end mod
  
          token = peek();
          if (token.getID() == Token.IN) {
              read();
          }
          else {
              error("foreach.in.expected", token);
          }
  
          Expression range = parseExpression();
          Expression endRange = null;
  
          token = peek();
          if (token.getID() == Token.DOTDOT) {
              read();
              endRange = parseExpression();
              token = peek();
          }
  
          if (endRange != null && foundASToken)
              error("foreach.as.not.allowed", asToken);
  
          boolean reverse = false;
          if (token.getID() == Token.REVERSE) {
              read();
              reverse = true;
              token = peek();
          }
  
          if (token.getID() == Token.RPAREN) {
              read();
          }
          else {
              error("foreach.rparen.expected", token);
          }
  
          Block body = parseBlock();
  
          info = info.setEndPosition(body.getSourceInfo());
  
          return new ForeachStatement
              (info, loopVar, range, endRange, reverse, body);
      }
docs: When this is called, the keyword "foreach" has already been read.