/* * Copyright 2016 Google Inc. * * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * 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.template.soy.soyparse; import static com.google.template.soy.soyparse.SoyFileParserTokenManager.lexStateNames; import java.util.NoSuchElementException; /** * A simple stack data structure for managing lexical states. * * <p>In the parser we need to enter/exit states recursively and an explicit stack is the best way * to go. We are using a custom datastructure to avoid boxing since this is called by the parser * very frequently. */ final class LexicalStateStack { // We could use a more space efficient encoding if necessary. For example, a bit set. // The universe of possible states is quite small <32.... but the depth of this stack should never // get bigger than ~30 so it probably doesn't matter. private int[] elements = new int[16]; private int size = 0; LexicalStateStack() {} /** Pushes a new item onto the stack. */ void push(int element) { if (element < 0 || element >= lexStateNames.length) { throw new IllegalArgumentException("Invalid lexical state: " + element); } // copy into locals to control reads and writes. int localSize = size; int[] localElements = elements; if (localSize + 1 == localElements.length) { localElements = doubleCapacity(); } localElements[localSize] = element; size = localSize + 1; } /** Removes the current head of the stack. */ int pop() { int localSize = size; if (localSize == 0) { throw new NoSuchElementException(); } size = localSize = localSize - 1; return elements[localSize]; } /** Removes all elements from the stack. */ void clear() { size = 0; } /** Returns the state at the top of the stack or -1 if the stack is empty. */ int peek() { int localSize = size; if (localSize == 0) { return -1; } return elements[localSize - 1]; } /** Doubles the capacity of the stack and returns it. */ private int[] doubleCapacity() { int oldCapacity = elements.length; int newCapacity = oldCapacity << 1; if (newCapacity < 0) { if (oldCapacity == Integer.MAX_VALUE) { throw new IllegalStateException("Sorry, stack too big"); } else { newCapacity = Integer.MAX_VALUE; } } int[] newElements = new int[newCapacity]; System.arraycopy(elements, 0, newElements, 0, oldCapacity); return elements = newElements; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('['); for (int i = 0; i < size; i++) { sb.append(SoyFileParserTokenManager.lexStateNames[elements[i]]); if (i < size - 1) { sb.append(", "); } } sb.append(']'); return sb.toString(); } }