All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.jexl3.internal.Scope Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.commons.jexl3.internal;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * A script scope, stores the declaration of parameters and local variables as symbols.
 * 

This also acts as the functional scope and variable definition store.

* @since 3.0 */ public final class Scope { /** * The value of an as-yet undeclared but variable, for instance: x; before var x;. */ static final Object UNDECLARED = new Object() { @Override public String toString() { return "??"; } }; /** * The value of a declared but undefined variable, for instance: var x;. */ static final Object UNDEFINED = new Object() { @Override public String toString() { return "?"; } }; /** * The parent scope. */ private final Scope parent; /** * The number of parameters. */ private int parms; /** * The number of local variables. */ private int vars; /** * The map of named variables aka script parameters and local variables. * Each parameter is associated to a symbol and is materialized as an offset in the stacked array used * during evaluation. */ private Map namedVariables = null; /** * The map of local captured variables to parent scope variables, ie closure. */ private Map capturedVariables = null; /** * Let symbols. */ private LexicalScope lexicalVariables = null; /** * The empty string array. */ private static final String[] EMPTY_STRS = {}; /** * Creates a new scope with a list of parameters. * @param scope the parent scope if any * @param parameters the list of parameters */ public Scope(final Scope scope, final String... parameters) { if (parameters != null) { parms = parameters.length; namedVariables = new LinkedHashMap<>(); for (int p = 0; p < parms; ++p) { namedVariables.put(parameters[p], p); } } else { parms = 0; } vars = 0; parent = scope; } /** * Checks whether an identifier is a local variable or argument, ie a symbol. * If this fails, look in parents for symbol that can be captured. * @param name the symbol name * @return the symbol index */ public Integer getSymbol(final String name) { return getSymbol(name, true); } /** * Checks whether an identifier is a local variable or argument, ie a symbol. * @param name the symbol name * @param capture whether solving by capturing a parent symbol is allowed * @return the symbol index */ private Integer getSymbol(final String name, final boolean capture) { Integer register = namedVariables != null ? namedVariables.get(name) : null; if (register == null && capture && parent != null) { final Integer pr = parent.getSymbol(name, true); if (pr != null) { if (capturedVariables == null) { capturedVariables = new LinkedHashMap<>(); } if (namedVariables == null) { namedVariables = new LinkedHashMap<>(); } register = namedVariables.size(); namedVariables.put(name, register); capturedVariables.put(register, pr); } } return register; } /** * Marks a symbol as a lexical, declared through let or const. * @param s the symbol * @return true if the symbol was not already present in the lexical set */ public boolean addLexical(final int s) { if (lexicalVariables == null) { lexicalVariables = new LexicalScope(); } return lexicalVariables.addSymbol(s); } /** * Checks whether a symbol is declared through a let or const. * @param s the symbol * @return true if symbol was declared through let or const */ public boolean isLexical(final int s) { return lexicalVariables != null && s >= 0 && lexicalVariables.hasSymbol(s); } /** * Checks whether a given symbol is captured. * @param symbol the symbol number * @return true if captured, false otherwise */ public boolean isCapturedSymbol(final int symbol) { return capturedVariables != null && capturedVariables.containsKey(symbol); } /** * Declares a parameter. *

* This method creates an new entry in the symbol map. *

* @param name the parameter name * @return the register index storing this variable */ public int declareParameter(final String name) { if (namedVariables == null) { namedVariables = new LinkedHashMap<>(); } else if (vars > 0) { throw new IllegalStateException("cant declare parameters after variables"); } Integer register = namedVariables.get(name); if (register == null) { register = namedVariables.size(); namedVariables.put(name, register); parms += 1; } return register; } /** * Declares a local variable. *

* This method creates an new entry in the symbol map. *

* @param name the variable name * @return the register index storing this variable */ public int declareVariable(final String name) { if (namedVariables == null) { namedVariables = new LinkedHashMap<>(); } Integer register = namedVariables.get(name); if (register == null) { register = namedVariables.size(); namedVariables.put(name, register); vars += 1; // check if local is redefining captured if (parent != null) { final Integer pr = parent.getSymbol(name, true); if (pr != null) { if (capturedVariables == null) { capturedVariables = new LinkedHashMap<>(); } capturedVariables.put(register, pr); } } } return register; } /** * Creates a frame by copying values up to the number of parameters. *

This captures the captured variables values.

* @param frame the caller frame * @param args the arguments * @return the arguments array */ public Frame createFrame(final Frame frame, final Object...args) { if (namedVariables == null) { return null; } final Object[] arguments = new Object[namedVariables.size()]; Arrays.fill(arguments, UNDECLARED); if (frame != null && capturedVariables != null && parent != null) { for (final Map.Entry capture : capturedVariables.entrySet()) { final Integer target = capture.getKey(); final Integer source = capture.getValue(); final Object arg = frame.get(source); arguments[target] = arg; } } return new Frame(this, arguments, 0).assign(args); } /** * Gets the captured index of a given symbol, ie the target index of a symbol in a child scope. * @param symbol the symbol index * @return the target symbol index or null if the symbol is not captured */ public Integer getCaptured(final int symbol) { if (capturedVariables != null) { for (final Map.Entry capture : capturedVariables.entrySet()) { final Integer source = capture.getValue(); if (source == symbol) { return capture.getKey(); } } } return null; } /** * Gets the index of a captured symbol, ie the source index of a symbol in a parent scope. * @param symbol the symbol index * @return the source symbol index or -1 if the symbol is not captured */ public int getCaptureDeclaration(final int symbol) { Integer declared = capturedVariables != null? capturedVariables.get(symbol) : null; return declared != null? declared.intValue() : -1; } /** * Gets this script captured symbols names, i.e. local variables defined in outer scopes and used * by this scope. * @return the captured names */ public String[] getCapturedVariables() { if (capturedVariables != null) { final List captured = new ArrayList<>(vars); for (final Map.Entry entry : namedVariables.entrySet()) { final int symnum = entry.getValue(); if (symnum >= parms && capturedVariables.containsKey(symnum)) { captured.add(entry.getKey()); } } if (!captured.isEmpty()) { return captured.toArray(new String[0]); } } return EMPTY_STRS; } /** * Gets the (maximum) number of arguments this script expects. * @return the number of parameters */ public int getArgCount() { return parms; } /** * Gets this script symbols names, i.e. parameters and local variables. * @return the symbol names */ public String[] getSymbols() { return namedVariables != null ? namedVariables.keySet().toArray(new String[0]) : EMPTY_STRS; } /** * Gets this script parameters, i.e. symbols assigned before creating local variables. * @return the parameter names */ public String[] getParameters() { return getParameters(0); } /** * Gets this script parameters. * @param bound number of known bound parameters (curry) * @return the parameter names */ String[] getParameters(final int bound) { final int unbound = parms - bound; if ((namedVariables == null) || (unbound <= 0)) { return EMPTY_STRS; } final String[] pa = new String[unbound]; int p = 0; for (final Map.Entry entry : namedVariables.entrySet()) { final int argn = entry.getValue(); if (argn >= bound && argn < parms) { pa[p++] = entry.getKey(); } } return pa; } /** * Gets this script local variable, i.e. symbols assigned to local variables excluding captured variables. * @return the local variable names */ public String[] getLocalVariables() { if ((namedVariables == null) || (vars <= 0)) { return EMPTY_STRS; } final List locals = new ArrayList<>(vars); for (final Map.Entry entry : namedVariables.entrySet()) { final int symnum = entry.getValue(); if (symnum >= parms && (capturedVariables == null || !capturedVariables.containsKey(symnum))) { locals.add(entry.getKey()); } } return locals.toArray(new String[0]); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy