org.openquark.cal.eclipse.embedded.analyzer.BindingVisitor.java Source code

Java tutorial

Introduction

Here is the source code for org.openquark.cal.eclipse.embedded.analyzer.BindingVisitor.java

Source

/*******************************************************************************
 * Copyright (c) 2007 Business Objects Software Limited and others.
 * All rights reserved. 
 * This file is made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Business Objects Software Limited - initial API and implementation
 *******************************************************************************/

/*
 * BindingTrackingSourceModelTraverser.java
 * Creation date: (Feb 14, 2006)
 * By: James Wright
 * 
 * Moved to the CAL_Eclipse_EmbeddedEditor project by Andrew Eisenberg
 * June 20, 2007
 */
package org.openquark.cal.eclipse.embedded.analyzer;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;

import org.apache.commons.collections.ArrayStack;
import org.openquark.cal.compiler.FieldName;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.compiler.ModuleNameResolver;
import org.openquark.cal.compiler.QualifiedName;
import org.openquark.cal.compiler.SourceMetricsManager;
import org.openquark.cal.compiler.SourceModel;
import org.openquark.cal.compiler.SourceModelTraverser;
import org.openquark.cal.compiler.SourceRange;
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.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;

/**
 * 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 BindingVisitor<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 currentBindings;

    /**
     * ArrayStacks of Maps (String -> LocalFunctionIdentifier) that map from
     * bindings of local function names currently in scope to the corresponding
     * LocalFunctionIdentifier.
     */
    private final ArrayStack 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 */
    final Map usingFunctionNames = new HashMap();

    /** Map (String -> QualifiedName) from datacons names declared in using clauses to the QualifiedName of the entity that they refer to */
    final Map usingDataconsNames = new HashMap();

    /** Map (String -> QualifiedName) from typecons names declared in using clauses to the QualifiedName of the entity that they refer to */
    final Map usingTypeconsNames = new HashMap();

    /** Map (String -> QualifiedName) from type class names declared in using clauses to the QualifiedName of the entity that they refer to */
    final Map usingTypeClassNames = new HashMap();

    /**
     * A base class for implementing algorithms that handle the processing of bindings for local definitions (both local function
     * definitions and local pattern match declarations).
     * 
     * @param <T> the argument type. If the visitation argument is not used, specify {@link Void}.
     * @param <R> the return type. If the return value is not used, specify {@link Void}.
     * 
     * @author Joseph Wong
     */
    static abstract class LocalBindingsProcessor<T, R> extends SourceModelTraverser<T, R> {

        /**
         * Processes a local definition binding.
         * @param name the name being bound.
         * @param localDefinition the source element corresponding to the definition/binding.
         * @param arg any visitation argument.
         */
        abstract void processLocalDefinitionBinding(final String name,
                final SourceModel.SourceElement localDefinition, final T arg);

        /**
         * Performs additional processing for a local function definition.
         * @param function the local function definition.
         * @param arg any visitation argument.
         */
        void additionallyProcessLocalDefnFunctionDefinition(final LocalDefn.Function.Definition function,
                final T arg) {
        }

        /**
         * Performs additional processing for a pattern-bound variable in a local pattern match declaration.
         * @param var the pattern-bound variable.
         * @param arg any visitation argument.
         */
        void additionallyProcessPatternVar(final Pattern.Var var, final T arg) {
        }

        /**
         * Performs additional processing for a punned field pattern in a local pattern mathc declaration.
         * @param fieldName the punned field name.
         * @param fieldNameSourceRange the source range for the field name. Can be null.
         * @param arg any visitation argument.
         */
        void additionallyProcessPunnedTextualFieldPattern(final FieldName.Textual fieldName,
                final SourceRange fieldNameSourceRange, final T arg) {
        }

        /**
         * {@inheritDoc}
         */
        public R visit_LocalDefn_Function_Definition(final LocalDefn.Function.Definition function, final T arg) {
            processLocalDefinitionBinding(function.getName(), function, arg); // the function defn is the bound element
            additionallyProcessLocalDefnFunctionDefinition(function, arg);
            return null;
        }

        /**
         * {@inheritDoc}
         */
        public R visit_Pattern_Var(final Pattern.Var var, final T arg) {
            processLocalDefinitionBinding(var.getName(), var, arg); // the pattern var is the bound element
            additionallyProcessPatternVar(var, arg);
            return null;
        }

        /**
         * {@inheritDoc}
         */
        public R visit_FieldPattern(final FieldPattern fieldPattern, final T arg) {
            // Handle punning
            if (fieldPattern.getPattern() == null) {
                // punning.

                // Textual field names become Vars of the same name.
                // Ordinal field names become wildcards ("_").
                final FieldName fieldName = fieldPattern.getFieldName().getName();
                if (fieldName instanceof FieldName.Textual) {
                    processLocalDefinitionBinding(fieldName.getCalSourceForm(), fieldPattern, arg); // the field pattern is the bound element
                    additionallyProcessPunnedTextualFieldPattern((FieldName.Textual) fieldName,
                            SourceMetricsManager.getSourceRange(fieldPattern.getFieldName()), arg);
                }
            }

            // call the superclass impl to reach the pattern and visit it (if it is non-null)
            return super.visit_FieldPattern(fieldPattern, arg);
        }

        /**
         * {@inheritDoc}
         */
        public R visit_LocalDefn_PatternMatch_UnpackDataCons(
                final LocalDefn.PatternMatch.UnpackDataCons unpackDataCons, final T arg) {
            // visit only the patterns
            unpackDataCons.getArgBindings().accept(this, arg);
            return null;
        }

        /**
         * {@inheritDoc}
         */
        public R visit_LocalDefn_PatternMatch_UnpackListCons(
                final LocalDefn.PatternMatch.UnpackListCons unpackListCons, final T arg) {
            // visit only the patterns
            unpackListCons.getHeadPattern().accept(this, arg);
            unpackListCons.getTailPattern().accept(this, arg);
            return null;
        }

        /**
         * {@inheritDoc}
         */
        public R visit_LocalDefn_PatternMatch_UnpackRecord(final LocalDefn.PatternMatch.UnpackRecord unpackRecord,
                final T arg) {
            // visit only the field patterns (and not the base record pattern - since we do not support them in local pattern match decl)
            final int nFieldPatterns = unpackRecord.getNFieldPatterns();
            for (int i = 0; i < nFieldPatterns; i++) {
                unpackRecord.getNthFieldPattern(i).accept(this, arg);
            }
            return null;
        }

        /**
         * {@inheritDoc}
         */
        public R visit_LocalDefn_PatternMatch_UnpackTuple(final LocalDefn.PatternMatch.UnpackTuple unpackTuple,
                final T arg) {
            // visit only the patterns
            final int nPatterns = unpackTuple.getNPatterns();
            for (int i = 0; i < nPatterns; i++) {
                unpackTuple.getNthPattern(i).accept(this, arg);
            }
            return null;
        }
    }

    public BindingVisitor() {
        moduleName = null;
        localFunctionIdentifierGenerator = new LocalFunctionIdentifierGenerator();
        currentBindings = new ArrayStack();
        currentLocalFunctionIdentifierBindings = new ArrayStack();
    }

    /** {@inheritDoc} */
    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.
     * 
     */
    public R visit_Import(Import importStmt, Object arg) {

        ModuleName importedModuleName = SourceModel.Name.Module.toModuleName(importStmt.getImportedModuleName());
        UsingItem[] usingItems = importStmt.getUsingItems();

        for (int i = 0, nUsingItems = usingItems.length; i < nUsingItems; i++) {
            // 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.
            usingItems[i].accept(this, importedModuleName);
        }

        return null;
    }

    /** {@inheritDoc} */
    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 (int i = 0, nNames = usingNames.length; i < nNames; i++) {
                usingFunctionNames.put(usingNames[i], QualifiedName.make(importedModuleName, usingNames[i]));
            }
        }

        return super.visit_Import_UsingItem_Function(usingItemFunction, arg);
    }

    /** {@inheritDoc} */
    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 (int i = 0, nNames = usingNames.length; i < nNames; i++) {
                usingDataconsNames.put(usingNames[i], QualifiedName.make(importedModuleName, usingNames[i]));
            }
        }

        return super.visit_Import_UsingItem_DataConstructor(usingItemDataConstructor, arg);
    }

    /** {@inheritDoc} */
    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 (int i = 0, nNames = usingNames.length; i < nNames; i++) {
                usingTypeconsNames.put(usingNames[i], QualifiedName.make(importedModuleName, usingNames[i]));
            }
        }

        return super.visit_Import_UsingItem_TypeConstructor(usingItemTypeConstructor, arg);
    }

    /** {@inheritDoc} */
    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 (int i = 0, nNames = usingNames.length; i < nNames; i++) {
                usingTypeClassNames.put(usingNames[i], QualifiedName.make(importedModuleName, usingNames[i]));
            }
        }

        return super.visit_Import_UsingItem_TypeClass(usingItemTypeClass, arg);
    }

    /** {@inheritDoc} */
    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} */
    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} */
    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 LocalBindingsProcessor<LinkedHashSet<String>, R> {

            /**
             * {@inheritDoc}
             */
            void processLocalDefinitionBinding(final String name, final SourceModel.SourceElement localDefinition,
                    final LinkedHashSet<String> arg) {
                addLocalDefinitionBinding(name, localDefinition);
            }

            /**
             * {@inheritDoc}
             */
            void additionallyProcessPatternVar(final Pattern.Var var, final LinkedHashSet<String> patternVarNames) {
                patternVarNames.add(var.getName());
            }

            /**
             * {@inheritDoc}
             */
            void additionallyProcessPunnedTextualFieldPattern(final FieldName.Textual fieldName,
                    final SourceRange fieldNameSourceRange, 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(makeTempVarNameForDesugaredLocalPatternMatchDecl(patternVarNames),
                        patternMatchDecl);
            }

            /**
             * {@inheritDoc}
             */
            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}
             */
            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}
             */
            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}
             */
            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} */
    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} */
    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} */
    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} */
    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} */
    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 currentScope = (Map) 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 currentIdentifierScope = (Map) 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());
        currentLocalFunctionIdentifierBindings.push(new HashMap());
    }

    /**
     * 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 (QualifiedName) usingFunctionNames.get(unqualifiedName);
        }

        if (name instanceof Name.DataCons && usingDataconsNames.containsKey(unqualifiedName)) {
            return (QualifiedName) usingDataconsNames.get(unqualifiedName);
        }

        if (name instanceof Name.TypeCons && usingTypeconsNames.containsKey(unqualifiedName)) {
            return (QualifiedName) usingTypeconsNames.get(unqualifiedName);
        }

        if (name instanceof Name.TypeClass && usingTypeClassNames.containsKey(unqualifiedName)) {
            return (QualifiedName) usingTypeClassNames.get(unqualifiedName);
        }

        if (name instanceof Name.WithoutContextCons) {
            if (usingDataconsNames.containsKey(unqualifiedName)) {
                return (QualifiedName) usingDataconsNames.get(unqualifiedName);
            }

            if (usingTypeconsNames.containsKey(unqualifiedName)) {
                return (QualifiedName) usingTypeconsNames.get(unqualifiedName);
            }

            if (usingTypeClassNames.containsKey(unqualifiedName)) {
                return (QualifiedName) usingTypeClassNames.get(unqualifiedName);
            }
        }

        return QualifiedName.make(getModuleName(), unqualifiedName);
    }

    /** @return A clone of the LocalFunctionIdentifierGenerator */
    LocalFunctionIdentifierGenerator getLocalFunctionNameGenerator() {
        return (LocalFunctionIdentifierGenerator) localFunctionIdentifierGenerator.clone();
    }

    /** 
     * @return True if name is currently bound, or false otherwise.
     * @param name String name to check
     */
    protected boolean isBound(String name) {

        for (int i = 0, nBindings = currentBindings.size(); i < nBindings; i++) {
            Map currentScope = (Map) 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 currentScope = (Map) 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 currentScope = (Map) currentBindings.peek(i);
            Object sourceElement = currentScope.get(name);

            if (sourceElement == null) {
                continue;
            }

            if (!(sourceElement instanceof LocalDefn.Function || sourceElement instanceof Pattern.Var
                    || sourceElement instanceof FieldPattern)) {
                return null;
            }

            Map currentIdentifierScope = (Map) currentLocalFunctionIdentifierBindings.peek(i);
            return (LocalFunctionIdentifier) currentIdentifierScope.get(name);
        }

        return null;
    }

    /**
     * Constructs a name, based on the pattern-bound variable names, for the synthetic local function for
     * hosting the defining expression of the pattern match declaration, which is to be added to the desugared tree.
     * 
     * @param patternVarNames a Collection of the pattern variable names, in source order.
     * @return a name for the synthetic local function.
     */
    static String makeTempVarNameForDesugaredLocalPatternMatchDecl(Collection/*String*/ patternVarNames) {
        final StringBuffer nameBuffer = new StringBuffer("$pattern");
        for (Iterator it = patternVarNames.iterator(); it.hasNext();) {
            final String patternVarName = (String) it.next();
            nameBuffer.append('_').append(patternVarName);
        }
        return nameBuffer.toString();
    }

}