/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* BindingTrackingSourceModelTraverser.java
* Creation date: (Feb 14, 2006)
* By: James Wright
*/
package org.openquark.cal.compiler;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import org.openquark.cal.compiler.SourceModel.ArgBindings;
import org.openquark.cal.compiler.SourceModel.FieldPattern;
import org.openquark.cal.compiler.SourceModel.Import;
import org.openquark.cal.compiler.SourceModel.LocalDefn;
import org.openquark.cal.compiler.SourceModel.ModuleDefn;
import org.openquark.cal.compiler.SourceModel.Name;
import org.openquark.cal.compiler.SourceModel.Pattern;
import org.openquark.cal.compiler.SourceModel.SourceElement;
import org.openquark.cal.compiler.SourceModel.Expr.Lambda;
import org.openquark.cal.compiler.SourceModel.Expr.Let;
import org.openquark.cal.compiler.SourceModel.Expr.Case.Alt.UnpackDataCons;
import org.openquark.cal.compiler.SourceModel.Expr.Case.Alt.UnpackListCons;
import org.openquark.cal.compiler.SourceModel.Expr.Case.Alt.UnpackRecord;
import org.openquark.cal.compiler.SourceModel.Expr.Case.Alt.UnpackTuple;
import org.openquark.cal.compiler.SourceModel.FunctionDefn.Algebraic;
import org.openquark.cal.compiler.SourceModel.Import.UsingItem;
import org.openquark.cal.compiler.SourceModel.LocalDefn.Function.Definition;
import org.openquark.cal.util.ArrayStack;
/**
* An implementation of SourceModelTraverser that tracks the current local definitions that
* are in scope. The current top-level function is considered to be in scope.
*
* @param <R> the return type. If the return value is not used, specify {@link Void}.
*
* @author James Wright
*/
class BindingTrackingSourceModelTraverser<R> extends SourceModelTraverser<Object, R> {
/**
* Name of the module currently being processed.
* This will be set when a ModuleDefn is visited.
*/
private ModuleName moduleName;
/**
* ArrayStack of Maps (String -> SourceElement) that map from bindings
* currently in scope to the corresponding source element.
*/
private final ArrayStack<Map<String, SourceElement>> currentBindings;
/**
* ArrayStacks of Maps (String -> LocalFunctionIdentifier) that map from
* bindings of local function names currently in scope to the corresponding
* LocalFunctionIdentifier.
*/
private final ArrayStack<Map<String, LocalFunctionIdentifier>> currentLocalFunctionIdentifierBindings;
/** Used for generating LocalFunctionIdentifiers when we encounter local functions. */
private final LocalFunctionIdentifierGenerator localFunctionIdentifierGenerator;
/** Map (String -> QualifiedName) from function names declared in using clauses to the QualifiedName of the entity that they refer to */
private final Map<String, QualifiedName> usingFunctionNames = new HashMap<String, QualifiedName>();
/** Map (String -> QualifiedName) from datacons names declared in using clauses to the QualifiedName of the entity that they refer to */
private final Map<String, QualifiedName> usingDataconsNames = new HashMap<String, QualifiedName>();
/** Map (String -> QualifiedName) from typecons names declared in using clauses to the QualifiedName of the entity that they refer to */
private final Map<String, QualifiedName> usingTypeconsNames = new HashMap<String, QualifiedName>();
/** Map (String -> QualifiedName) from type class names declared in using clauses to the QualifiedName of the entity that they refer to */
private final Map<String, QualifiedName> usingTypeClassNames = new HashMap<String, QualifiedName>();
BindingTrackingSourceModelTraverser() {
moduleName = null;
localFunctionIdentifierGenerator = new LocalFunctionIdentifierGenerator();
currentBindings = ArrayStack.make();
currentLocalFunctionIdentifierBindings = ArrayStack.make();
}
/** {@inheritDoc} */
@Override
public R visit_ModuleDefn(ModuleDefn defn, Object arg) {
// Save modulename for clients
moduleName = SourceModel.Name.Module.toModuleName(defn.getModuleName());
return super.visit_ModuleDefn(defn, arg);
}
/**
* {@inheritDoc}
* This implementation of visitImport passes the name of the imported module
* to each of its child UsingItems.
*
*/
@Override
public R visit_Import(Import importStmt, Object arg) {
ModuleName importedModuleName = SourceModel.Name.Module.toModuleName(importStmt.getImportedModuleName());
UsingItem[] usingItems = importStmt.getUsingItems();
for (final UsingItem usingItem : usingItems) {
// We pass the name of the imported module to using clause visitors so that
// they can fill the using maps; we're overriding the default traversal method
// here, so we don't call the superclass method.
usingItem.accept(this, importedModuleName);
}
return null;
}
/** {@inheritDoc} */
@Override
public R visit_Import_UsingItem_Function(UsingItem.Function usingItemFunction, Object arg) {
if(arg != null && arg instanceof ModuleName) {
// Fill up the using maps for name resolution
ModuleName importedModuleName = (ModuleName)arg;
String[] usingNames = usingItemFunction.getUsingNames();
for (final String usingName : usingNames) {
usingFunctionNames.put(usingName, QualifiedName.make(importedModuleName, usingName));
}
}
return super.visit_Import_UsingItem_Function(usingItemFunction, arg);
}
/** {@inheritDoc} */
@Override
public R visit_Import_UsingItem_DataConstructor(UsingItem.DataConstructor usingItemDataConstructor, Object arg) {
if(arg != null && arg instanceof ModuleName) {
// Fill up the using maps for name resolution
ModuleName importedModuleName = (ModuleName)arg;
String[] usingNames = usingItemDataConstructor.getUsingNames();
for (final String usingName : usingNames) {
usingDataconsNames.put(usingName, QualifiedName.make(importedModuleName, usingName));
}
}
return super.visit_Import_UsingItem_DataConstructor(usingItemDataConstructor, arg);
}
/** {@inheritDoc} */
@Override
public R visit_Import_UsingItem_TypeConstructor(UsingItem.TypeConstructor usingItemTypeConstructor, Object arg) {
if(arg != null && arg instanceof ModuleName) {
// Fill up the using maps for name resolution
ModuleName importedModuleName = (ModuleName)arg;
String[] usingNames = usingItemTypeConstructor.getUsingNames();
for (final String usingName : usingNames) {
usingTypeconsNames.put(usingName, QualifiedName.make(importedModuleName, usingName));
}
}
return super.visit_Import_UsingItem_TypeConstructor(usingItemTypeConstructor, arg);
}
/** {@inheritDoc} */
@Override
public R visit_Import_UsingItem_TypeClass(UsingItem.TypeClass usingItemTypeClass, Object arg) {
if(arg != null && arg instanceof ModuleName) {
// Fill up the using maps for name resolution
ModuleName importedModuleName = (ModuleName)arg;
String[] usingNames = usingItemTypeClass.getUsingNames();
for (final String usingName : usingNames) {
usingTypeClassNames.put(usingName, QualifiedName.make(importedModuleName, usingName));
}
}
return super.visit_Import_UsingItem_TypeClass(usingItemTypeClass, arg);
}
/** {@inheritDoc} */
@Override
public R visit_FunctionDefn_Algebraic(Algebraic algebraic, Object arg) {
enterScope();
localFunctionIdentifierGenerator.reset(algebraic.getName());
for(int i = 0; i < algebraic.getNParameters(); i++) {
SourceModel.Parameter param = algebraic.getNthParameter(i);
addRegularBinding(param.getName(), param);
}
R ret = super.visit_FunctionDefn_Algebraic(algebraic, arg);
localFunctionIdentifierGenerator.reset(null);
leaveScope();
return ret;
}
/** {@inheritDoc} */
@Override
public R visit_Expr_Lambda(Lambda lambda, Object arg) {
enterScope();
for(int i = 0; i < lambda.getNParameters(); i++) {
SourceModel.Parameter param = lambda.getNthParameter(i);
addRegularBinding(param.getName(), param);
}
R ret = super.visit_Expr_Lambda(lambda, arg);
leaveScope();
return ret;
}
/** {@inheritDoc} */
@Override
public R visit_Expr_Let(Let let, Object arg) {
enterScope();
// Let expressions are mutually recursive, so we want to bind the
// function names before we enter the new scope associated with each local function.
/**
* Handles the adding of bindings for local definitions (both local functions and local pattern match declarations).
* This is done by walking the local definitions and adding a binding for each local function / pattern-bound variable
* encountered. The synthetic local function generated by the compiler for desugaring a local pattern match declaration
* is also taken into account.
*
* @author Joseph Wong
*/
class LocallyDefinedNamesCollector extends IdentifierResolver.LocalBindingsProcessor<LinkedHashSet<String>, R> {
/**
* {@inheritDoc}
*/
@Override
void processLocalDefinitionBinding(final String name, final SourceModel.SourceElement localDefinition, final LinkedHashSet<String> arg) {
addLocalDefinitionBinding(name, localDefinition);
}
/**
* {@inheritDoc}
*/
@Override
void additionallyProcessPatternVar(final Pattern.Var var, final LinkedHashSet<String> patternVarNames) {
patternVarNames.add(var.getName());
}
/**
* {@inheritDoc}
*/
@Override
void additionallyProcessPunnedTextualRecordFieldPattern(final FieldName.Textual fieldName, final Name.Field fieldNameElement, final LinkedHashSet<String> patternVarNames) {
patternVarNames.add(fieldName.getCalSourceForm());
}
/**
* {@inheritDoc}
*/
@Override
void additionallyProcessPunnedTextualDataConsFieldPattern(final FieldName.Textual fieldName, final Name.Field fieldNameElement, final Name.DataCons dataConsName, final LinkedHashSet<String> patternVarNames) {
patternVarNames.add(fieldName.getCalSourceForm());
}
/**
* Adds an additional binding for the synthetic local function which is generated by the compiler to host the defining
* expression of a local pattern match declaration. This is done to keep the local function identifier generator in
* sync with what the compiler would do.
*
* @param patternMatchDecl the pattern match declaration.
* @param patternVarNames the LinkedHashSet of the pattern variable names, in source order.
*/
private void addBindingForSyntheticLocalDefinition(final LocalDefn.PatternMatch patternMatchDecl, final LinkedHashSet<String> patternVarNames) {
addLocalDefinitionBinding(FreeVariableFinder.makeTempVarNameForDesugaredLocalPatternMatchDecl(patternVarNames), patternMatchDecl);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_LocalDefn_PatternMatch_UnpackDataCons(final LocalDefn.PatternMatch.UnpackDataCons unpackDataCons, final LinkedHashSet<String> arg) {
// visit only the patterns
final LinkedHashSet<String> patternVarNames = new LinkedHashSet<String>();
super.visit_LocalDefn_PatternMatch_UnpackDataCons(unpackDataCons, patternVarNames);
// add the synthetic definition last
addBindingForSyntheticLocalDefinition(unpackDataCons, patternVarNames);
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_LocalDefn_PatternMatch_UnpackListCons(final LocalDefn.PatternMatch.UnpackListCons unpackListCons, final LinkedHashSet<String> arg) {
// visit only the patterns
final LinkedHashSet<String> patternVarNames = new LinkedHashSet<String>();
super.visit_LocalDefn_PatternMatch_UnpackListCons(unpackListCons, patternVarNames);
// add the synthetic definition last
addBindingForSyntheticLocalDefinition(unpackListCons, patternVarNames);
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_LocalDefn_PatternMatch_UnpackRecord(final LocalDefn.PatternMatch.UnpackRecord unpackRecord, final LinkedHashSet<String> arg) {
// visit only the field patterns (and not the base record pattern - since we do not support them in local pattern match decl)
final LinkedHashSet<String> patternVarNames = new LinkedHashSet<String>();
super.visit_LocalDefn_PatternMatch_UnpackRecord(unpackRecord, patternVarNames);
// add the synthetic definition last
addBindingForSyntheticLocalDefinition(unpackRecord, patternVarNames);
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_LocalDefn_PatternMatch_UnpackTuple(final LocalDefn.PatternMatch.UnpackTuple unpackTuple, final LinkedHashSet<String> arg) {
// visit only the patterns
final LinkedHashSet<String> patternVarNames = new LinkedHashSet<String>();
super.visit_LocalDefn_PatternMatch_UnpackTuple(unpackTuple, patternVarNames);
// add the synthetic definition last
addBindingForSyntheticLocalDefinition(unpackTuple, patternVarNames);
return null;
}
}
// Use the LocallyDefinedNamesCollector to visit the let definitions
final LocallyDefinedNamesCollector locallyDefinedNamesCollector = new LocallyDefinedNamesCollector();
final int nLocalFunctions = let.getNLocalDefinitions();
for (int i = 0; i < nLocalFunctions; i++) {
let.getNthLocalDefinition(i).accept(locallyDefinedNamesCollector, null);
}
// Now call the superclass implementation to walk through the let expression with the right name bindings
R ret = super.visit_Expr_Let(let, arg);
leaveScope();
return ret;
}
/** {@inheritDoc} */
@Override
public R visit_LocalDefn_Function_Definition(Definition function, Object arg) {
enterScope();
for(int i = 0; i < function.getNParameters(); i++) {
SourceModel.Parameter param = function.getNthParameter(i);
addRegularBinding(param.getName(), param);
}
R ret = super.visit_LocalDefn_Function_Definition(function, arg);
leaveScope();
return ret;
}
/** {@inheritDoc} */
@Override
public R visit_Expr_Case_Alt_UnpackDataCons(UnpackDataCons cons, Object arg) {
enterScope();
handleArgBindings(cons.getArgBindings());
R ret = super.visit_Expr_Case_Alt_UnpackDataCons(cons, arg);
leaveScope();
return ret;
}
/**
* Adds all of the bindings in argBindings to the current scope.
*
* visitCaseExprUnpackDataConsAlt and visitCaseExprUnpackDataConsGroupAlt both
* do exactly the same (rather involved) processing on their arg bindings. This
* common processing is factored into handleArgBindings.
*
* @param argBindings ArgBindings the bindings to process
*/
private void handleArgBindings(ArgBindings argBindings) {
if(argBindings instanceof ArgBindings.Matching) {
ArgBindings.Matching matchingArgBindings = (ArgBindings.Matching)argBindings;
for (int i = 0; i < matchingArgBindings.getNFieldPatterns(); i++) {
FieldPattern fieldPattern = matchingArgBindings.getNthFieldPattern(i);
Pattern pattern = fieldPattern.getPattern();
if (pattern == null) {
// punning.
// Textual field names become Vars of the same name.
// Ordinal field names become wildcards ("_").
FieldName fieldName = fieldPattern.getFieldName().getName();
if (fieldName instanceof FieldName.Textual) {
pattern = Pattern.Var.make(fieldName.getCalSourceForm());
}
}
if (pattern instanceof Pattern.Var) {
Pattern.Var patternVar = (Pattern.Var)pattern;
addRegularBinding(patternVar.getName(), patternVar);
}
}
} else if (argBindings instanceof ArgBindings.Positional) {
ArgBindings.Positional positionalArgBindings = (ArgBindings.Positional)argBindings;
for (int i = 0; i < positionalArgBindings.getNPatterns(); i++) {
Pattern pattern = positionalArgBindings.getNthPattern(i);
if (pattern instanceof Pattern.Var) {
Pattern.Var patternVar = (Pattern.Var)pattern;
addRegularBinding(patternVar.getName(), patternVar);
}
}
}
}
/** {@inheritDoc} */
@Override
public R visit_Expr_Case_Alt_UnpackListCons(UnpackListCons cons, Object arg) {
enterScope();
if(cons.getHeadPattern() instanceof Pattern.Var) {
Pattern.Var patternVar = (Pattern.Var)cons.getHeadPattern();
addRegularBinding(patternVar.getName(), patternVar);
}
if(cons.getTailPattern() instanceof Pattern.Var) {
Pattern.Var patternVar = (Pattern.Var)cons.getTailPattern();
addRegularBinding(patternVar.getName(), patternVar);
}
R ret = super.visit_Expr_Case_Alt_UnpackListCons(cons, arg);
leaveScope();
return ret;
}
/** {@inheritDoc} */
@Override
public R visit_Expr_Case_Alt_UnpackRecord(UnpackRecord record, Object arg) {
enterScope();
for(int i = 0; i < record.getNFieldPatterns(); i++) {
FieldPattern pattern = record.getNthFieldPattern(i);
if(pattern.getPattern() != null &&
pattern.getPattern() instanceof Pattern.Var) {
Pattern.Var patternVar = (Pattern.Var)pattern.getPattern();
addRegularBinding(patternVar.getName(), patternVar);
} else if (pattern.getPattern() == null) {
addRegularBinding(pattern.getFieldName().getName().getCalSourceForm(), pattern);
}
}
if(record.getBaseRecordPattern() != null) {
if(record.getBaseRecordPattern() instanceof Pattern.Var) {
Pattern.Var patternVar = (Pattern.Var)record.getBaseRecordPattern();
addRegularBinding(patternVar.getName(), patternVar);
}
}
R ret = super.visit_Expr_Case_Alt_UnpackRecord(record, arg);
leaveScope();
return ret;
}
/** {@inheritDoc} */
@Override
public R visit_Expr_Case_Alt_UnpackTuple(UnpackTuple tuple, Object arg) {
enterScope();
for(int i = 0; i < tuple.getNPatterns(); i++) {
Pattern pattern = tuple.getNthPattern(i);
if(pattern instanceof Pattern.Var) {
Pattern.Var patternVar = (Pattern.Var)pattern;
addRegularBinding(patternVar.getName(), patternVar);
}
}
R ret = super.visit_Expr_Case_Alt_UnpackTuple(tuple, arg);
leaveScope();
return ret;
}
/**
* Adds a binding to the current innermost scope
* @param name Name of the binding to add
* @param sourceElement SourceElement that name should be bound to
*/
private void addRegularBinding(String name, SourceModel.SourceElement sourceElement) {
Map<String, SourceElement> currentScope = currentBindings.peek();
currentScope.put(name, sourceElement);
}
/**
* Adds a binding to the current innermost scope and records the unique identifier for
* a local definition (a function or a pattern variable in a pattern match declaration).
* @param name String name of the local definition to add a binding for
* @param localDefinition SourceModel for the local function
*/
private void addLocalDefinitionBinding(String name, SourceModel.SourceElement localDefinition) {
addRegularBinding(name, localDefinition);
Map<String, LocalFunctionIdentifier> currentIdentifierScope = currentLocalFunctionIdentifierBindings.peek();
// Don't try to track local function identifiers without a module name and toplevel function
if(moduleName != null && getCurrentFunction() != null) {
LocalFunctionIdentifier localFunctionIdentifier = localFunctionIdentifierGenerator.generateLocalFunctionIdentifier(moduleName, name);
currentIdentifierScope.put(name, localFunctionIdentifier);
}
}
/**
* Adds a new scope to the current bindings.
*/
private void enterScope() {
currentBindings.push(new HashMap<String, SourceElement>());
currentLocalFunctionIdentifierBindings.push(new HashMap<String, LocalFunctionIdentifier>());
}
/**
* Removes the current scope from the bindings.
*/
private void leaveScope() {
currentBindings.pop();
currentLocalFunctionIdentifierBindings.pop();
}
/** @return The name of the currently in-scope function, if any */
String getCurrentFunction() {
return localFunctionIdentifierGenerator.getCurrentFunction();
}
/** @return The name of the module being processed */
ModuleName getModuleName() {
return moduleName;
}
/**
* @param name Name to fetch the qualifiedName for
* @param moduleNameResolver the module name resolver to use for resolving module names.
* @return QualifiedName of the top-level entity that name refers to, or null if name does not refer to
* a top-level entity.
*/
QualifiedName getQualifiedName(Name.Qualifiable name, ModuleNameResolver moduleNameResolver) {
String unqualifiedName = name.getUnqualifiedName();
if(name.getModuleName() != null) {
ModuleNameResolver.ResolutionResult resolution = moduleNameResolver.resolve(SourceModel.Name.Module.toModuleName(name.getModuleName()));
ModuleName resolvedModuleName = resolution.getResolvedModuleName();
return QualifiedName.make(resolvedModuleName, name.getUnqualifiedName());
}
if(isBound(unqualifiedName)) {
return null;
}
if(name instanceof Name.Function && usingFunctionNames.containsKey(unqualifiedName)) {
return usingFunctionNames.get(unqualifiedName);
}
if(name instanceof Name.DataCons && usingDataconsNames.containsKey(unqualifiedName)) {
return usingDataconsNames.get(unqualifiedName);
}
if(name instanceof Name.TypeCons && usingTypeconsNames.containsKey(unqualifiedName)) {
return usingTypeconsNames.get(unqualifiedName);
}
if(name instanceof Name.TypeClass && usingTypeClassNames.containsKey(unqualifiedName)) {
return usingTypeClassNames.get(unqualifiedName);
}
if(name instanceof Name.WithoutContextCons) {
if(usingDataconsNames.containsKey(unqualifiedName)) {
return usingDataconsNames.get(unqualifiedName);
}
if(usingTypeconsNames.containsKey(unqualifiedName)) {
return usingTypeconsNames.get(unqualifiedName);
}
if(usingTypeClassNames.containsKey(unqualifiedName)) {
return usingTypeClassNames.get(unqualifiedName);
}
}
return QualifiedName.make(getModuleName(), unqualifiedName);
}
/** @return A clone of the LocalFunctionIdentifierGenerator */
LocalFunctionIdentifierGenerator getLocalFunctionNameGenerator() {
return localFunctionIdentifierGenerator.clone();
}
/**
* @return True if name is currently bound, or false otherwise.
* @param name String name to check
*/
boolean isBound(String name) {
for(int i = 0, nBindings = currentBindings.size(); i < nBindings; i++) {
Map<String, SourceElement> currentScope = currentBindings.peek(i);
if(currentScope.containsKey(name)) {
return true;
}
}
return false;
}
/**
* @param name Name of binding to retrieve the defining SourceElement for
* @return The SourceElement that defines the current binding specified by
* name, or null if name is not currently bound.
*/
SourceModel.SourceElement getBoundElement(String name) {
for(int i = 0, nBindings = currentBindings.size(); i < nBindings; i++) {
Map<String, SourceElement> currentScope = currentBindings.peek(i);
Object sourceElement = currentScope.get(name);
if(sourceElement != null) {
return (SourceModel.SourceElement)sourceElement;
}
}
return null;
}
/**
* @param name Name of a local function definition that is current in scope
* @return The LocalFunctionIdentifier that corresponds to the current binding of name,
* or null if name is not currently bound to a local function.
*/
LocalFunctionIdentifier getBoundLocalFunctionIdentifier(String name) {
for(int i = 0, nBindings = currentBindings.size(); i < nBindings; i++) {
Map<String, SourceElement> currentScope = currentBindings.peek(i);
SourceElement sourceElement = currentScope.get(name);
if(sourceElement == null) {
continue;
}
if(!(sourceElement instanceof LocalDefn.Function || sourceElement instanceof Pattern.Var || sourceElement instanceof FieldPattern)) {
return null;
}
Map<String, LocalFunctionIdentifier> currentIdentifierScope = currentLocalFunctionIdentifierBindings.peek(i);
return currentIdentifierScope.get(name);
}
return null;
}
}