1 /*
  2  * Copyright (c) 2024, 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.incubator.code.parser.impl;
 27 
 28 import jdk.incubator.code.parser.impl.Position.LineMap;
 29 
 30 import java.nio.CharBuffer;
 31 import java.util.ArrayList;
 32 import java.util.List;
 33 
 34 import static jdk.incubator.code.parser.impl.Tokens.DUMMY;
 35 import static jdk.incubator.code.parser.impl.Tokens.Token;
 36 
 37 /**
 38  * The lexical analyzer maps an input stream consisting of
 39  * ASCII characters and Unicode escapes into a token sequence.
 40  *
 41  * <p><b>This is NOT part of any supported API.
 42  * If you write code that depends on this, you do so at your own risk.
 43  * This code and its internal interfaces are subject to change or
 44  * deletion without notice.</b>
 45  */
 46 public final class Scanner implements Lexer {
 47 
 48     private final Tokens tokens;
 49 
 50     /**
 51      * The token, set by nextToken().
 52      */
 53     private Token token;
 54 
 55     /**
 56      * The previous token, set by nextToken().
 57      */
 58     private Token prevToken;
 59 
 60     /**
 61      * Buffer of saved tokens (used during lookahead)
 62      */
 63     private final List<Token> savedTokens = new ArrayList<>();
 64 
 65     private final JavaBasedTokenizer tokenizer;
 66 
 67     Scanner(Factory fac, char[] buf, int inputLength) {
 68         this(fac, new JavaBasedTokenizer(fac, buf, inputLength));
 69     }
 70 
 71     Scanner(Factory fac, JavaBasedTokenizer tokenizer) {
 72         this.tokenizer = tokenizer;
 73         tokens = fac.tokens;
 74         token = prevToken = DUMMY;
 75     }
 76 
 77     public Token token() {
 78         return token(0);
 79     }
 80 
 81     public Token token(int lookahead) {
 82         if (lookahead == 0) {
 83             return token;
 84         } else {
 85             ensureLookahead(lookahead);
 86             return savedTokens.get(lookahead - 1);
 87         }
 88     }
 89 
 90     //where
 91     private void ensureLookahead(int lookahead) {
 92         for (int i = savedTokens.size(); i < lookahead; i++) {
 93             savedTokens.add(tokenizer.readToken());
 94         }
 95     }
 96 
 97     public Token prevToken() {
 98         return prevToken;
 99     }
100 
101     public void nextToken() {
102         prevToken = token;
103         if (!savedTokens.isEmpty()) {
104             token = savedTokens.remove(0);
105         } else {
106             token = tokenizer.readToken();
107         }
108     }
109 
110     public LineMap getLineMap() {
111         return tokenizer.getLineMap();
112     }
113 
114     public int errPos() {
115         return tokenizer.errPos();
116     }
117 
118     public void errPos(int pos) {
119         tokenizer.errPos(pos);
120     }
121 
122     public static final class Factory {
123         final Log log;
124         final Tokens tokens;
125 
126         /**
127          * Create a new scanner factory.
128          */
129         Factory() {
130             this.log = new Log();
131             this.tokens = new Tokens();
132         }
133 
134         public Scanner newScanner(CharSequence input) {
135             if (input instanceof CharBuffer charBuffer) {
136                 return newScanner(toArray(charBuffer), charBuffer.limit());
137             } else {
138                 char[] array = input.toString().toCharArray();
139                 return newScanner(array, array.length);
140             }
141         }
142 
143         public Scanner newScanner(char[] input, int inputLength) {
144             return new Scanner(this, input, inputLength);
145         }
146 
147         static char[] toArray(CharBuffer buffer) {
148             if (buffer.hasArray())
149                 return buffer.compact().flip().array();
150             else
151                 return buffer.toString().toCharArray();
152         }
153 
154         static final Factory INSTANCE = new Factory();
155     }
156 
157     public static Factory factory() {
158         return Factory.INSTANCE;
159     }
160 }