1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.internal.parser;
  27 
  28 /**
  29  * Utility for scanning thru a char array.
  30  *
  31  */
  32 public class Scanner {
  33     /** Characters to scan. */
  34     protected final char[] content;
  35 
  36     /** Position in content. */
  37     protected int position;
  38 
  39     /** Scan limit. */
  40     protected final int limit;
  41 
  42     /** Current line number. */
  43     protected int line;
  44 
  45     /** Current character in stream */
  46     protected char ch0;
  47     /** 1 character lookahead */
  48     protected char ch1;
  49     /** 2 character lookahead */
  50     protected char ch2;
  51     /** 3 character lookahead */
  52     protected char ch3;
  53 
  54     /**
  55      * Constructor
  56      *
  57      * @param content content to scan
  58      * @param line    start line number
  59      * @param start   position index in content where to start
  60      * @param length  length of input
  61      */
  62     protected Scanner(final char[] content, final int line, final int start, final int length) {
  63         this.content  = content;
  64         this.position = start;
  65         this.limit    = start + length;
  66         this.line     = line;
  67 
  68         reset(position);
  69     }
  70 
  71     /**
  72      * Constructor
  73      *
  74      * Scan content from beginning to end. Content given as a string
  75      *
  76      * @param content content to scan
  77      */
  78     protected Scanner(final String content) {
  79         this(content.toCharArray(), 0, 0, content.length());
  80     }
  81 
  82     /**
  83      * Copy constructor
  84      *
  85      * @param scanner  scanner
  86      * @param state    state, the state is a tuple {position, limit, line} only visible internally
  87      */
  88     Scanner(final Scanner scanner, final State state) {
  89         content  = scanner.content;
  90         position = state.position;
  91         limit    = state.limit;
  92         line     = state.line;
  93 
  94         reset(position);
  95    }
  96 
  97     /**
  98      * Information needed to restore previous state.
  99      */
 100     static class State {
 101         /** Position in content. */
 102         public final int position;
 103 
 104         /** Scan limit. */
 105         public int limit;
 106 
 107         /** Current line number. */
 108         public final int line;
 109 
 110         State(final int position, final int limit, final int line) {
 111             this.position = position;
 112             this.limit    = limit;
 113             this.line     = line;
 114         }
 115 
 116         /**
 117          * Change the limit for a new scanner.
 118          * @param limit New limit.
 119          */
 120         void setLimit(final int limit) {
 121             this.limit = limit;
 122         }
 123 
 124         boolean isEmpty() {
 125             return position == limit;
 126         }
 127     }
 128 
 129     /**
 130      * Save the state of the scan.
 131      * @return Captured state.
 132      */
 133     State saveState() {
 134         return new State(position, limit, line);
 135     }
 136 
 137     /**
 138      * Restore the state of the scan.
 139      * @param state Captured state.
 140      */
 141     void restoreState(final State state) {
 142         position = state.position;
 143         line     = state.line;
 144 
 145         reset(position);
 146     }
 147 
 148     /**
 149      * Returns true of scanner is at end of input
 150      * @return true if no more input
 151      */
 152     protected final boolean atEOF() {
 153         return position == limit;
 154     }
 155 
 156     /**
 157      * Get the ith character from the content.
 158      * @param i Index of character.
 159      * @return ith character or '\0' if beyond limit.
 160      */
 161     protected final char charAt(final int i) {
 162         // Get a character from the content, '\0' if beyond the end of file.
 163         return i < limit ? content[i] : '\0';
 164     }
 165 
 166     /**
 167      * Reset to a character position.
 168      * @param i Position in content.
 169      */
 170     protected final void reset(final int i) {
 171         ch0 = charAt(i);
 172         ch1 = charAt(i + 1);
 173         ch2 = charAt(i + 2);
 174         ch3 = charAt(i + 3);
 175         position = i < limit? i : limit;
 176     }
 177 
 178     /**
 179      * Skip ahead a number of characters.
 180      * @param n Number of characters to skip.
 181      */
 182     protected final void skip(final int n) {
 183         if (n == 1 && !atEOF()) {
 184             ch0 = ch1;
 185             ch1 = ch2;
 186             ch2 = ch3;
 187             ch3 = charAt(position + 4);
 188             position++;
 189         } else if (n != 0) {
 190             reset(position + n);
 191         }
 192     }
 193 }