1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.jexl3.internal;
18
19 import java.util.BitSet;
20
21 /**
22 * The set of symbols declared in a lexical scope.
23 * <p>The symbol identifiers are determined by the functional scope.</p>
24 * <p>We use 2 bits per symbol s; bit (s*2)+0 sets the actual symbol as lexical (let), bit (s*2)+1 as a const.
25 * There are actually only 2 used states: 1 and 3</p>
26 */
27 public class LexicalScope {
28 /**
29 * Number of bits in a long.
30 */
31 protected static final int BITS_PER_LONG = 64;
32 /**
33 * Bits per symbol.
34 * let (b + 0) + const (b + 1).
35 */
36 protected static final int BITS_PER_SYMBOL = 2;
37 /**
38 * From a symbol number to a starting symbol bit number.
39 */
40 protected static final int SYMBOL_SHIFT = BITS_PER_SYMBOL - 1;
41 /**
42 * Bitmask for symbols.
43 */
44 protected static final long SYMBOL_MASK = (1L << (BITS_PER_SYMBOL - 1)) - 1; // 3, as 1+2, 2 bits
45 /**
46 * Number of symbols.
47 */
48 protected int count = 0;
49 /**
50 * The mask of symbols in the scope.
51 */
52 protected long symbols = 0L;
53 /**
54 * Symbols after bit 64 (aka symbol 32 when 2 bits per symbol).
55 */
56 protected BitSet moreSymbols = null;
57
58 /**
59 * Create a scope.
60 */
61 public LexicalScope() {
62 }
63
64 /**
65 * Frame copy ctor base.
66 */
67 protected LexicalScope(final LexicalScope other) {
68 this.symbols = other.symbols;
69 final BitSet otherMoreSymbols = other.moreSymbols;
70 this.moreSymbols = otherMoreSymbols != null ? (BitSet) otherMoreSymbols.clone() : null;
71 this.count = other.count;
72 }
73
74 /**
75 * Ensures more symbols can be stored.
76 *
77 * @return the set of more symbols
78 */
79 private BitSet moreBits() {
80 if (moreSymbols == null) {
81 moreSymbols = new BitSet();
82 }
83 return moreSymbols;
84 }
85
86 /**
87 * Whether a given bit (not symbol) is set.
88 * @param bit the bit
89 * @return true if set
90 */
91 private boolean isSet(final int bit) {
92 if (bit < BITS_PER_LONG) {
93 return (symbols & (1L << bit)) != 0L;
94 }
95 return moreSymbols != null && moreSymbols.get(bit - BITS_PER_LONG);
96 }
97
98 /**
99 * Sets a given bit (not symbol).
100 * @param bit the bit
101 * @return true if it was actually set, false if it was set before
102 */
103 private boolean set(final int bit) {
104 if (bit < BITS_PER_LONG) {
105 if ((symbols & (1L << bit)) != 0L) {
106 return false;
107 }
108 symbols |= (1L << bit);
109 } else {
110 final int bit64 = bit - BITS_PER_LONG;
111 final BitSet ms = moreBits();
112 if (ms.get(bit64)) {
113 return false;
114 }
115 ms.set(bit64, true);
116 }
117 return true;
118 }
119
120 /**
121 * Checks whether a symbol has already been declared.
122 *
123 * @param symbol the symbol
124 * @return true if declared, false otherwise
125 */
126 public boolean hasSymbol(final int symbol) {
127 final int bit = symbol << SYMBOL_SHIFT;
128 return isSet(bit);
129 }
130
131 /**
132 * Checks whether a symbol is declared as a constant.
133 *
134 * @param symbol the symbol
135 * @return true if declared as constant, false otherwise
136 */
137 public boolean isConstant(final int symbol) {
138 final int bit = (symbol << SYMBOL_SHIFT) | 1;
139 return isSet(bit);
140 }
141
142 /**
143 * Adds a symbol in this scope.
144 *
145 * @param symbol the symbol
146 * @return true if registered, false if symbol was already registered
147 */
148 public boolean addSymbol(final int symbol) {
149 final int bit = (symbol << SYMBOL_SHIFT) ;
150 if (set(bit)) {
151 count += 1;
152 return true;
153 }
154 return false;
155 }
156
157 /**
158 * Adds a constant in this scope.
159 *
160 * @param symbol the symbol
161 * @return true if registered, false if symbol was already registered
162 */
163 public boolean addConstant(final int symbol) {
164 final int letb = (symbol << SYMBOL_SHIFT) ;
165 if (!isSet(letb)) {
166 throw new IllegalStateException("const not declared as symbol " + symbol);
167 }
168 final int bit = (symbol << SYMBOL_SHIFT) | 1;
169 return set(bit);
170 }
171
172 /**
173 * Clear all symbols.
174 *
175 * @param cleanSymbol a (optional, may be null) functor to call for each cleaned symbol
176 */
177 public final void clearSymbols(final java.util.function.IntConsumer cleanSymbol) {
178 // undefine symbols getting out of scope
179 if (cleanSymbol != null) {
180 long clean = symbols;
181 while (clean != 0L) {
182 final int bit = Long.numberOfTrailingZeros(clean);
183 final int s = bit >> SYMBOL_SHIFT;
184 cleanSymbol.accept(s);
185 // call clean for symbol definition (3 as a mask for 2 bits,1+2)
186 clean &= ~(SYMBOL_MASK << bit);
187 }
188 // step by bits per symbol
189 int bit = moreSymbols != null ? moreSymbols.nextSetBit(0) : -1;
190 while (bit >= 0) {
191 final int s = (bit + BITS_PER_LONG) >> SYMBOL_SHIFT;
192 cleanSymbol.accept(s);
193 bit = moreSymbols.nextSetBit(bit + BITS_PER_SYMBOL);
194 }
195 }
196 // internal cleansing
197 symbols = 0L;
198 count = 0;
199 if (moreSymbols != null) {
200 moreSymbols.clear();
201 }
202 }
203
204 /**
205 * @return the number of symbols defined in this scope.
206 */
207 public int getSymbolCount() {
208 return count;
209 }
210 }