/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.engine.scanner;
import java.util.Arrays;
/**
* Instances of the abstract class {@code KeywordState} represent a state in a state machine used to
* scan keywords.
*
* @coverage dart.engine.parser
*/
public final class KeywordState {
/**
* An empty transition table used by leaf states.
*/
private static final KeywordState[] EMPTY_TABLE = new KeywordState[26];
/**
* The initial state in the state machine.
*/
public static final KeywordState KEYWORD_STATE = createKeywordStateTable();
/**
* Create the next state in the state machine where we have already recognized the subset of
* strings in the given array of strings starting at the given offset and having the given length.
* All of these strings have a common prefix and the next character is at the given start index.
*
* @param start the index of the character in the strings used to transition to a new state
* @param strings an array containing all of the strings that will be recognized by the state
* machine
* @param offset the offset of the first string in the array that has the prefix that is assumed
* to have been recognized by the time we reach the state being built
* @param length the number of strings in the array that pass through the state being built
* @return the state that was created
*/
private static KeywordState computeKeywordStateTable(int start, String[] strings, int offset,
int length) {
KeywordState[] result = new KeywordState[26];
assert length != 0;
char chunk = '\0';
int chunkStart = -1;
boolean isLeaf = false;
for (int i = offset; i < offset + length; i++) {
if (strings[i].length() == start) {
isLeaf = true;
}
if (strings[i].length() > start) {
char c = strings[i].charAt(start);
if (chunk != c) {
if (chunkStart != -1) {
result[chunk - 'a'] = computeKeywordStateTable(start + 1, strings, chunkStart, i
- chunkStart);
}
chunkStart = i;
chunk = c;
}
}
}
if (chunkStart != -1) {
assert result[chunk - 'a'] == null;
result[chunk - 'a'] = computeKeywordStateTable(start + 1, strings, chunkStart, offset
+ length - chunkStart);
} else {
assert length == 1;
return new KeywordState(EMPTY_TABLE, strings[offset]);
}
if (isLeaf) {
return new KeywordState(result, strings[offset]);
} else {
return new KeywordState(result, null);
}
}
/**
* Create the initial state in the state machine.
*
* @return the state that was created
*/
private static KeywordState createKeywordStateTable() {
Keyword[] values = Keyword.values();
String[] strings = new String[values.length];
for (int i = 0; i < values.length; i++) {
strings[i] = values[i].getSyntax();
}
Arrays.sort(strings);
return computeKeywordStateTable(0, strings, 0, strings.length);
}
/**
* A table mapping characters to the states to which those characters will transition. (The index
* into the array is the offset from the character {@code 'a'} to the transitioning character.)
*/
private final KeywordState[] table;
/**
* The keyword that is recognized by this state, or {@code null} if this state is not a terminal
* state.
*/
private final Keyword keyword;
/**
* Initialize a newly created state to have the given transitions and to recognize the keyword
* with the given syntax.
*
* @param table a table mapping characters to the states to which those characters will transition
* @param syntax the syntax of the keyword that is recognized by the state
*/
private KeywordState(KeywordState[] table, String syntax) {
this.table = table;
this.keyword = (syntax == null) ? null : Keyword.keywords.get(syntax);
}
/**
* Return the keyword that was recognized by this state, or {@code null} if this state does not
* recognized a keyword.
*
* @return the keyword that was matched by reaching this state
*/
public Keyword keyword() {
return keyword;
}
/**
* Return the state that follows this state on a transition of the given character, or
* {@code null} if there is no valid state reachable from this state with such a transition.
*
* @param c the character used to transition from this state to another state
* @return the state that follows this state on a transition of the given character
*/
public KeywordState next(char c) {
return table[c - 'a'];
}
}