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 * https://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 functional scope determines the symbol identifiers.</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 /**
30 * Number of bits in a long.
31 */
32 protected static final int BITS_PER_LONG = 64;
33
34 /**
35 * Bits per symbol.
36 * let (b + 0) + const (b + 1).
37 */
38 protected static final int BITS_PER_SYMBOL = 2;
39
40 /**
41 * From a symbol number to a starting symbol bit number.
42 */
43 protected static final int SYMBOL_SHIFT = BITS_PER_SYMBOL - 1;
44
45 /**
46 * Bitmask for symbols.
47 */
48 protected static final long SYMBOL_MASK = (1L << BITS_PER_SYMBOL - 1) - 1; // 3, as 1+2, 2 bits
49
50 /**
51 * Number of symbols.
52 */
53 protected int count;
54
55 /**
56 * The mask of symbols in the scope.
57 */
58 protected long symbols;
59
60 /**
61 * Symbols after bit 64 (aka symbol 32 when 2 bits per symbol).
62 */
63 protected BitSet moreSymbols;
64
65 /**
66 * Create a scope.
67 */
68 public LexicalScope() {
69 }
70
71 /**
72 * Frame copy ctor base.
73 */
74 protected LexicalScope(final LexicalScope other) {
75 this.symbols = other.symbols;
76 final BitSet otherMoreSymbols = other.moreSymbols;
77 this.moreSymbols = otherMoreSymbols != null ? (BitSet) otherMoreSymbols.clone() : null;
78 this.count = other.count;
79 }
80
81 /**
82 * Adds a constant in this scope.
83 *
84 * @param symbol the symbol
85 * @return true if registered, false if symbol was already registered
86 */
87 public boolean addConstant(final int symbol) {
88 final int letb = symbol << SYMBOL_SHIFT ;
89 if (!isSet(letb)) {
90 throw new IllegalStateException("const not declared as symbol " + symbol);
91 }
92 final int bit = symbol << SYMBOL_SHIFT | 1;
93 return set(bit);
94 }
95
96 /**
97 * Adds a symbol in this scope.
98 *
99 * @param symbol the symbol
100 * @return true if registered, false if symbol was already registered
101 */
102 public boolean addSymbol(final int symbol) {
103 final int bit = symbol << SYMBOL_SHIFT ;
104 if (set(bit)) {
105 count += 1;
106 return true;
107 }
108 return false;
109 }
110
111 /**
112 * Clear all symbols.
113 *
114 * @param cleanSymbol a (optional, may be null) functor to call for each cleaned symbol
115 */
116 public final void clearSymbols(final java.util.function.IntConsumer cleanSymbol) {
117 // undefine symbols getting out of scope
118 if (cleanSymbol != null) {
119 long clean = symbols;
120 while (clean != 0L) {
121 final int bit = Long.numberOfTrailingZeros(clean);
122 final int s = bit >> SYMBOL_SHIFT;
123 cleanSymbol.accept(s);
124 // call clean for symbol definition (3 as a mask for 2 bits,1+2)
125 clean &= ~(SYMBOL_MASK << bit);
126 }
127 // step by bits per symbol
128 int bit = moreSymbols != null ? moreSymbols.nextSetBit(0) : -1;
129 while (bit >= 0) {
130 final int s = bit + BITS_PER_LONG >> SYMBOL_SHIFT;
131 cleanSymbol.accept(s);
132 bit = moreSymbols.nextSetBit(bit + BITS_PER_SYMBOL);
133 }
134 }
135 // internal cleansing
136 symbols = 0L;
137 count = 0;
138 if (moreSymbols != null) {
139 moreSymbols.clear();
140 }
141 }
142
143 /**
144 * @return the number of symbols defined in this scope.
145 */
146 public int getSymbolCount() {
147 return count;
148 }
149
150 /**
151 * Checks whether a symbol has already been declared.
152 *
153 * @param symbol the symbol
154 * @return true if declared, false otherwise
155 */
156 public boolean hasSymbol(final int symbol) {
157 final int bit = symbol << SYMBOL_SHIFT;
158 return isSet(bit);
159 }
160
161 /**
162 * Checks whether a symbol is declared as a constant.
163 *
164 * @param symbol the symbol
165 * @return true if declared as constant, false otherwise
166 */
167 public boolean isConstant(final int symbol) {
168 final int bit = symbol << SYMBOL_SHIFT | 1;
169 return isSet(bit);
170 }
171
172 /**
173 * Tests whether a given bit (not symbol) is set.
174 *
175 * @param bit the bit
176 * @return true if set
177 */
178 private boolean isSet(final int bit) {
179 if (bit < BITS_PER_LONG) {
180 return (symbols & 1L << bit) != 0L;
181 }
182 return moreSymbols != null && moreSymbols.get(bit - BITS_PER_LONG);
183 }
184
185 /**
186 * Ensures more symbols can be stored.
187 *
188 * @return the set of more symbols
189 */
190 private BitSet moreBits() {
191 if (moreSymbols == null) {
192 moreSymbols = new BitSet();
193 }
194 return moreSymbols;
195 }
196
197 /**
198 * Sets a given bit (not symbol).
199 *
200 * @param bit the bit
201 * @return true if it was actually set, false if it was set before
202 */
203 private boolean set(final int bit) {
204 if (bit < BITS_PER_LONG) {
205 if ((symbols & 1L << bit) != 0L) {
206 return false;
207 }
208 symbols |= 1L << bit;
209 } else {
210 final int bit64 = bit - BITS_PER_LONG;
211 final BitSet ms = moreBits();
212 if (ms.get(bit64)) {
213 return false;
214 }
215 ms.set(bit64, true);
216 }
217 return true;
218 }
219 }