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.ArrayDeque;
20 import java.util.Deque;
21
22 /**
23 * The set of valued symbols defined in a lexical frame.
24 * <p>The functional scope determines the symbol identifiers. Since the frame contains values of
25 * all symbols in the functional scope, the lexical frame preserves values of symbols reused for local
26 * definition.
27 */
28 public class LexicalFrame extends LexicalScope {
29
30 /**
31 * The script frame.
32 */
33 private final Frame frame;
34
35 /**
36 * Previous frame.
37 */
38 protected final LexicalFrame previous;
39
40 /**
41 * The stack of values in the lexical frame.
42 * <p>[symbol identifier, value] are stacked in pairs</p>
43 */
44 private Deque<Object> stack;
45
46 /**
47 * Lexical frame ctor.
48 *
49 * @param scriptf the script frame
50 * @param outerf the previous lexical frame
51 */
52 public LexicalFrame(final Frame scriptf, final LexicalFrame outerf) {
53 this.previous = outerf;
54 this.frame = scriptf;
55 }
56
57 /**
58 * Copy ctor.
59 *
60 * @param src the frame to copy
61 */
62 public LexicalFrame(final LexicalFrame src) {
63 super(src);
64 frame = src.frame;
65 previous = src.previous;
66 stack = src.stack != null ? new ArrayDeque<>(src.stack) : null;
67 }
68
69 /**
70 * Define the arguments.
71 *
72 * @return this frame
73 */
74 public LexicalFrame defineArgs() {
75 if (frame != null) {
76 final int argc = frame.getScope().getArgCount();
77 for (int a = 0; a < argc; ++a) {
78 super.addSymbol(a);
79 }
80 }
81 return this;
82 }
83
84 /**
85 * Defines a symbol.
86 *
87 * @param symbol the symbol to define
88 * @param capture whether this redefines a captured symbol
89 * @return true if the symbol is defined, false otherwise
90 */
91 public boolean defineSymbol(final int symbol, final boolean capture) {
92 final boolean declared = addSymbol(symbol);
93 if (declared && capture) {
94 if (stack == null) {
95 stack = new ArrayDeque<>();
96 }
97 stack.push(symbol);
98 Object value = frame.get(symbol);
99 if (value == null) {
100 value = this;
101 }
102 stack.push(value);
103 }
104 return declared;
105 }
106
107 /**
108 * Pops back values and lexical frame.
109 *
110 * @return the previous frame
111 */
112 public LexicalFrame pop() {
113 // undefine all symbols
114 clearSymbols(s -> frame.set(s, Scope.UNDEFINED));
115 // restore values of captured symbols that were overwritten
116 if (stack != null) {
117 while (!stack.isEmpty()) {
118 Object value = stack.pop();
119 if (value == Scope.UNDECLARED) {
120 value = Scope.UNDEFINED;
121 } else if (value == this) {
122 value = null;
123 }
124 final int symbol = (Integer) stack.pop();
125 frame.set(symbol, value);
126 }
127 }
128 return previous;
129 }
130
131 }