/* * 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. */ /* * IdentifierOccurrence.java * Created: Sep 11, 2007 * By: Joseph Wong */ package org.openquark.cal.compiler; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.openquark.cal.compiler.SourceModel.Name; import org.openquark.cal.compiler.SourceModel.SourceElement; /** * This is the root class of a hierarchy of classes representing different kinds of identifier * occurrences in CAL. Each subclass of this class represents the <i>identity</i> of the CAL entity * referred to by the identifier, as well as, the <i>context</i> in which the identifier appears in * source. (The entity identity is encapsulated by an instance of {@link IdentifierInfo}.) Also, * each identifier occurrence may be associated with a * {@link org.openquark.cal.compiler.SourceModel.SourceElement SourceElement} and a {@link SourceRange}, * if the occurrence corresponds to an identifier appearing in source. * * <p> * For example, the name of a top-level function in its definition must be unqualified, while a * reference to the same function in an expression is <i>qualifiable</i> (can be qualified) - both * occurrences would be associated with the same {@link IdentifierInfo}. These two occurrences can * be distinguished by their representing {@link IdentifierOccurrence} - the former would be an * {@link org.openquark.cal.compiler.IdentifierOccurrence.Binding.Definition IdentifierOccurrence.Binding.Definition}, * while the latter would be an * {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference.Qualifiable IdentifierOccurrence.Reference.Qualifiable}. * * <p> * At the same time, some concepts which are grammatically similar are distinguished by different subclasses, e.g. * a punned ordinal data constructor field name vs a punned textual data constructor field name - the former is a simple * reference to a data constructor field, while the latter is also a binding of a local variable. * * <p> * The hierarchy of classes and what they represent are as follows: * * <ul> * <li>a name binding (which binds a name to an entity in a particular scope) * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Binding} * <ul> * <li>a definition (the actual definition in source of the entity to which the name is bound) * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Binding.Definition Definition} * <li>a name listed in an {@code import using} clause * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Binding.ImportUsingItem ImportUsingItem} * <li>an external name binding (one that does not appear in the source code being analyzed, e.g. one that * is implied by the information in a {@link ModuleTypeInfo} or a {@link CodeQualificationMap}) * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Binding.External External} * <li>a data constructor field name appearing in the definition of its associated data constructor * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Binding.DataConsFieldName DataConsFieldName} * <li>a punned textual data constructor field name (which is a local variable binding) * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Binding.PunnedTextualDataConsFieldName PunnedTextualDataConsFieldName} * <li>a punned textual record field name (which is a local variable binding) * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Binding.PunnedTextualRecordFieldName PunnedTextualRecordFieldName} * </ul> * <li>a reference (to an entity in scope) * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference Reference} * <ul> * <li>a reference to a module (whether the name can be partially qualified, or must be fully qualified) * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference.Module Module} * <li>a reference to a top-level (scoped) entity in a context where qualification is allowed or required * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference.Qualifiable Qualifiable} * <li>a reference to a top-level (scoped) entity in a context where qualification is <i>not</i> allowed * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference.NonQualifiable NonQualifiable} * <li>a reference to a locally-bound entity * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference.Local Local} * <li>a reference to a data constructor field (including occurrences of all associated data constructors) * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference.DataConsFieldName DataConsFieldName} * <ul> * <li>...a field name not appearing in a punned context * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference.DataConsFieldName.NonPunned NonPunned} * <li>...a punned ordinal field name * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference.DataConsFieldName.PunnedOrdinal PunnedOrdinal} * </ul> * <li>a reference to a record field * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference.RecordFieldName RecordFieldName} * <ul> * <li>...a field name not appearing in a punned context * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference.RecordFieldName.NonPunned NonPunned} * <li>...a punned ordinal field name * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference.RecordFieldName.PunnedOrdinal PunnedOrdinal} * </ul> * <li>an operator reference (to a function, class method, type constructor, or data constructor) * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference.Operator Operator} * <li>a reference to a type variable * - {@link org.openquark.cal.compiler.IdentifierOccurrence.Reference.TypeVariable TypeVariable} * </ul> * <li>a foreign descriptor (or external name) for a foreign entity * - {@link org.openquark.cal.compiler.IdentifierOccurrence.ForeignDescriptor ForeignDescriptor} * </ul> * * @see IdentifierInfo * IdentifierInfo - * this is the closely related class representing the identity of the entity being referred to by an occurrence, * independent of the context in which it appears * * @author Joseph Wong * @author Iulian Radu * @author Peter Cardwell */ /* * @history * * The representation of the different kinds of occurrences via a class hierarchy (as opposed to an enum-based representation) * is inspired by the SourceModel, by Bo Ilic. * * Some of the kinds of occurrences (in particular: module names and qualifiable scoped entity names) have also been * represented by the SourceIdentifier class, by Iulian Radu and Peter Cardwell; and by the AnalysedIdentifier class, * by Iulian Radu. */ /* * @discussion * * One of the design choices in the creation of IdentifierOccurrence is to make it mostly orthogonal to the underlying identity * of the entity being referred to, which is represented by IdentifierInfo - hence there are two class hierarchies rather * than one intertwined hierarchy. At the same time, certain kinds of occurrences only make sense for certain kinds of names * (e.g. a qualifiable reference can only refer to top-level (scoped) entities), thus classes in the IdentifierOccurrence * hierarchy are parameterized by the kinds of IdentifierInfo allowable. */ // todo-jowong revisit the decision to parameterize on the kind of identifier in an occurrence public abstract class IdentifierOccurrence<I extends IdentifierInfo> { /** * Abstract base class for an occurrence which is a name binding (binding a name to an entity in a particular scope). * * @author Joseph Wong */ public static abstract class Binding<I extends IdentifierInfo> extends IdentifierOccurrence<I> { /** * Represents a definition (the actual definition in source of the entity to which the name is bound). * * @author Joseph Wong * @author Iulian Radu * @author Peter Cardwell */ public static final class Definition<I extends IdentifierInfo> extends Binding<I> { /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. This may be more than just a name, * but can be the entire definition of an entity. Can be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private Definition(final I identifierInfo, final SourceElement sourceElement, final SourceRange sourceRange) { super(identifierInfo, sourceElement, sourceRange); } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. This may be more than just a name, * but can be the entire definition of an entity. Can be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. * @return an instance of this class. */ static <I extends IdentifierInfo> Definition<I> make(final I identifierInfo, final SourceElement sourceElement, final SourceRange sourceRange) { return new Definition<I>(identifierInfo, sourceElement, sourceRange); } } /** * Represents a name listed in an {@code import using} clause. * * @author Joseph Wong */ public static final class ImportUsingItem<I extends IdentifierInfo.TopLevel> extends Binding<I> { /** * The index into the array of names in the {@code import using} clause. */ // todo-jowong this won't be needed when the source model is refactored to represent unqualified names. private final int itemIndex; /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. This may be more than just a name, * but can be the entire definition of an entity. Can be null. * @param itemIndex the index into the array of names in the {@code import using} clause. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private ImportUsingItem(final I identifierInfo, final SourceModel.Import.UsingItem sourceElement, final int itemIndex, final SourceRange sourceRange) { super(identifierInfo, sourceElement, sourceRange); this.itemIndex = itemIndex; } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. This may be more than just a name, * but can be the entire definition of an entity. Can be null. * @param itemIndex the index into the array of names in the {@code import using} clause. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. * @return an instance of this class. */ static <I extends IdentifierInfo.TopLevel> ImportUsingItem<I> make(final I identifierInfo, final SourceModel.Import.UsingItem sourceElement, final int itemIndex, final SourceRange sourceRange) { return new ImportUsingItem<I>(identifierInfo, sourceElement, itemIndex, sourceRange); } /** * @return the index into the array of names in the {@code import using} clause. */ public int getItemIndex() { return itemIndex; } /** * {@inheritDoc} */ @Override public String toString() { return toStringWithCustomContent(" itemIndex=" + itemIndex); } } /** * Represents an external name binding (one that does not appear in the source code being analyzed, e.g. one that * is implied by the information in a {@link ModuleTypeInfo} or a {@link CodeQualificationMap}). * * @author Joseph Wong */ public static final class External<I extends IdentifierInfo> extends Binding<I> { /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. */ private External(final I identifierInfo) { super(identifierInfo, null, null); } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @return an instance of this class. */ static <I extends IdentifierInfo> External<I> make(final I identifierInfo) { return new External<I>(identifierInfo); } } /** * Represents a data constructor field name appearing in the definition of its associated data constructor. * * @author Joseph Wong */ public static final class DataConsFieldName extends Binding<IdentifierInfo.TopLevel.DataConsFieldName> { /** * The definition of the data constructor defining this field. Can *not* be null. */ private final Binding.Definition<IdentifierInfo.TopLevel.DataCons> dataConsOccurrence; /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. This may be more than just a name, * but can be the entire definition of an entity. Can be null. * @param dataConsOccurrence the definition of the data constructor defining this field. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private DataConsFieldName( final IdentifierInfo.TopLevel.DataConsFieldName identifierInfo, final Binding.Definition<IdentifierInfo.TopLevel.DataCons> dataConsOccurrence, final Name.Field sourceElement, final SourceRange sourceRange) { super(identifierInfo, sourceElement, sourceRange); if (dataConsOccurrence == null) { throw new NullPointerException(); } this.dataConsOccurrence = dataConsOccurrence; } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. This may be more than just a name, * but can be the entire definition of an entity. Can be null. * @param dataConsOccurrence the definition of the data constructor defining this field. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. * @return an instance of this class. */ static DataConsFieldName make( final IdentifierInfo.TopLevel.DataConsFieldName identifierInfo, final Binding.Definition<IdentifierInfo.TopLevel.DataCons> dataConsOccurrence, final Name.Field sourceElement, final SourceRange sourceRange) { return new DataConsFieldName(identifierInfo, dataConsOccurrence, sourceElement, sourceRange); } /** * @return the definition of the data constructor defining this field. Can *not* be null. */ public Binding.Definition<IdentifierInfo.TopLevel.DataCons> getDataConsOccurrence() { return dataConsOccurrence; } /** * {@inheritDoc} */ @Override public String toString() { return toStringWithCustomContent(" dataConsOccurrence=" + dataConsOccurrence); } } /** * Represents a punned textual data constructor field name (which is a local variable binding). * The associated identifier info should be either a case patter variable or a local pattern match variable. * * <p> * A data constructor field name can be associated with multiple data constructors when it appears * in a case alternative containing more than one data constructor, e.g. * <pre> * case foo of * (DC1|DC2|DC3) {field1=bar, field2} -> ... * </pre> * Both field1 and field2 are associated with DC1, DC2, and DC3, and field2 is a punned data constructor field name. * * @author Joseph Wong */ public static final class PunnedTextualDataConsFieldName<I extends IdentifierInfo.Local> extends Binding<I> { /** * The list of bindings for the data constructors associated with the field name. Can *not* be null. * The list should be non-empty. If the list has more than one data constructor, the ordering should * following that of the case alternative. */ private final List<Binding<IdentifierInfo.TopLevel.DataCons>> dataConsNameBindings; /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param dataConsNameBindings the list of bindings for the data constructors associated with the field name. * Can *not* be null. The list should be non-empty. * @param sourceElement the source element associated with the occurrence. This may be more than just a name, * but can be the entire definition of an entity. Can be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private PunnedTextualDataConsFieldName( final I identifierInfo, final List<Binding<IdentifierInfo.TopLevel.DataCons>> dataConsNameBindings, final SourceElement sourceElement, final SourceRange sourceRange) { super(identifierInfo, sourceElement, sourceRange); if (dataConsNameBindings == null) { throw new NullPointerException(); } this.dataConsNameBindings = Collections.unmodifiableList(new ArrayList<Binding<IdentifierInfo.TopLevel.DataCons>>(dataConsNameBindings)); } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param dataConsNameBindings the list of bindings for the data constructors associated with the field name. * Can *not* be null. The list should be non-empty. * @param sourceElement the source element associated with the occurrence. This may be more than just a name, * but can be the entire definition of an entity. Can be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. * @return an instance of this class. */ static <I extends IdentifierInfo.Local> PunnedTextualDataConsFieldName<I> make( final I identifierInfo, final List<Binding<IdentifierInfo.TopLevel.DataCons>> dataConsNameBindings, final SourceElement sourceElement, final SourceRange sourceRange) { return new PunnedTextualDataConsFieldName<I>(identifierInfo, dataConsNameBindings, sourceElement, sourceRange); } /** * @return the list of bindings for the data constructors associated with the field name. Can *not* be null. * The list should be non-empty. If the list has more than one data constructor, the ordering should * following that of the case alternative. */ public List<Binding<IdentifierInfo.TopLevel.DataCons>> getDataConsNameBindings() { return dataConsNameBindings; } /** * {@inheritDoc} */ @Override public String toString() { return toStringWithCustomContent(" dataConsNameBindings=" + dataConsNameBindings); } } /** * Represents a punned textual record field name (which is a local variable binding). * The associated identifier info should be either a case patter variable or a local pattern match variable. * * @author Joseph Wong */ public static final class PunnedTextualRecordFieldName<I extends IdentifierInfo.Local> extends Binding<I> { /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. This may be more than just a name, * but can be the entire definition of an entity. Can be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private PunnedTextualRecordFieldName(final I identifierInfo, final SourceElement sourceElement, final SourceRange sourceRange) { super(identifierInfo, sourceElement, sourceRange); } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. This may be more than just a name, * but can be the entire definition of an entity. Can be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. * @return an instance of this class. */ static <I extends IdentifierInfo.Local> PunnedTextualRecordFieldName<I> make(final I identifierInfo, final SourceElement sourceElement, final SourceRange sourceRange) { return new PunnedTextualRecordFieldName<I>(identifierInfo, sourceElement, sourceRange); } } /** * The source element associated with the occurrence. This may be more than just a name, but * can be the entire definition of an entity. * * Can be null. */ private final SourceElement sourceElement; /** * Private constructor. Intended for subclasses only. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. This may be more than just a name, * but can be the entire definition of an entity. Can be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private Binding(final I identifierInfo, final SourceElement sourceElement, final SourceRange sourceRange) { super(identifierInfo, sourceRange); this.sourceElement = sourceElement; } /** * {@inheritDoc} */ @Override public final SourceElement getSourceElement() { return sourceElement; } } /** * Abstract base class for an occurrence which is a reference (to an entity in scope). * Unlike the other occurrence types, an instance of a class in this hierarchy *must* be associated with a source element. * * @author Joseph Wong */ public static abstract class Reference<I extends IdentifierInfo> extends IdentifierOccurrence<I> { /** * Represents a reference to a module (whether the name can be partially qualified, or must be fully qualified). * * @author Joseph Wong * @author Iulian Radu * @author Peter Cardwell */ public static final class Module extends Reference<IdentifierInfo.Module> { /** * Whether the name must be fully qualified or can be partially qualified. */ private final boolean mustBeFullyQualified; /** * The source element associated with the occurrence. Can *not* be null. * This should be either a * {@link org.openquark.cal.compiler.SourceModel.Name.Module SourceModel.Name.Module} or a * {@link org.openquark.cal.compiler.SourceModel.Name.WithoutContextCons SourceModel.Name.WithoutContextCons}. */ private final Name sourceElement; /** * Constructs an instance of this identifier occurrence. * @param mustBeFullyQualified whether the name must be fully qualified or can be partially qualified. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private Module(final boolean mustBeFullyQualified, final IdentifierInfo.Module identifierInfo, final Name sourceElement, final SourceRange sourceRange) { super(identifierInfo, sourceRange); if (sourceElement == null) { throw new NullPointerException(); } this.mustBeFullyQualified = mustBeFullyQualified; this.sourceElement = sourceElement; } /** * Factory method for constructing an instance of this identifier occurrence. * @param mustBeFullyQualified whether the name must be fully qualified or can be partially qualified. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can be null. * @return an instance of this class. */ static Module make(final boolean mustBeFullyQualified, final IdentifierInfo.Module identifierInfo, final Name.Module sourceElement) { return new Module(mustBeFullyQualified, identifierInfo, sourceElement, sourceElement.getSourceRange()); } /** * Factory method for constructing an instance of this identifier occurrence. * @param mustBeFullyQualified whether the name must be fully qualified or can be partially qualified. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can be null. * @return an instance of this class. */ static Module make(final boolean mustBeFullyQualified, final IdentifierInfo.Module identifierInfo, final Name.WithoutContextCons sourceElement) { return new Module(mustBeFullyQualified, identifierInfo, sourceElement, sourceElement.getSourceRange()); } /** * @return whether the name must be fully qualified or can be partially qualified. */ public boolean mustBeFullyQualified() { return mustBeFullyQualified; } /** * {@inheritDoc} * * @return the source element associated with the occurrence. Can *not* be null. This should be either a * {@link org.openquark.cal.compiler.SourceModel.Name.Module SourceModel.Name.Module} or a * {@link org.openquark.cal.compiler.SourceModel.Name.WithoutContextCons SourceModel.Name.WithoutContextCons}. */ @Override public Name getSourceElement() { return sourceElement; } /** * {@inheritDoc} */ @Override public String toString() { return toStringWithCustomContent(" mustBeFullyQualified=" + mustBeFullyQualified); } } /** * Represents a reference to a top-level (scoped) entity in a context where qualification is allowed or required. * * @author Joseph Wong * @author Iulian Radu * @author Peter Cardwell */ public static final class Qualifiable<I extends IdentifierInfo.TopLevel> extends Reference<I> { /** * Whether the reference appears in an expression context (i.e. the reference itself is an expression). */ private final boolean isExpressionContext; /** * The occurrence of the module name within this occurrence. * Can be null if this occurrence is an unqualified (but qualifiable) occurrence. */ private final Reference.Module moduleNameOccurrence; /** * The source element associated with the occurrence. Can *not* be null. */ private final Name.Qualifiable sourceElement; /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param isExpressionContext whether the reference appears in an expression context (i.e. the reference itself is an expression). * @param moduleNameOccurrence the occurrence of the module name within this occurrence. * Can be null if this occurrence is an unqualified (but qualifiable) occurrence. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private Qualifiable(final I identifierInfo, final boolean isExpressionContext, final Reference.Module moduleNameOccurrence, final Name.Qualifiable sourceElement, final SourceRange sourceRange) { super(identifierInfo, sourceRange); this.isExpressionContext = isExpressionContext; this.moduleNameOccurrence = moduleNameOccurrence; if (sourceElement == null) { throw new NullPointerException(); } this.sourceElement = sourceElement; } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param isExpressionContext whether the reference appears in an expression context (i.e. the reference itself is an expression). * @param moduleNameOccurrence the occurrence of the module name within this occurrence. * Can be null if this occurrence is an unqualified (but qualifiable) occurrence. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @return an instance of this class. */ static <I extends IdentifierInfo.TopLevel> Qualifiable<I> make(final I identifierInfo, final boolean isExpressionContext, final Reference.Module moduleNameOccurrence, final Name.Qualifiable sourceElement) { return new Qualifiable<I>(identifierInfo, isExpressionContext, moduleNameOccurrence, sourceElement, sourceElement.getSourceRange()); } /** * @return true if the reference appears in an expression context; false otherwise. */ public boolean isExpressionContext() { return isExpressionContext; } /** * @return the occurrence of the module name within this occurrence. * Can be null if this occurrence is an unqualified (but qualifiable) occurrence. */ public Reference.Module getModuleNameOccurrence() { return moduleNameOccurrence; } /** * {@inheritDoc} * * @return the source element associated with the occurrence. Can *not* be null. */ @Override public Name.Qualifiable getSourceElement() { return sourceElement; } /** * {@inheritDoc} */ @Override public String toString() { return toStringWithCustomContent(" isExpressionContext=" + isExpressionContext + " moduleNameOccurrence=" + moduleNameOccurrence); } } /** * Represents a reference to a top-level (scoped) entity in a context where qualification is <i>not</i> allowed. * * @author Joseph Wong */ public static final class NonQualifiable<I extends IdentifierInfo> extends Reference<I> { /** * The source element associated with the occurrence. Can *not* be null. */ private final SourceElement sourceElement; /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private NonQualifiable(final I identifierInfo, final SourceElement sourceElement, final SourceRange sourceRange) { super(identifierInfo, sourceRange); if (sourceElement == null) { throw new NullPointerException(); } this.sourceElement = sourceElement; } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. * @return an instance of this class. */ static <I extends IdentifierInfo> NonQualifiable<I> make(final I identifierInfo, final SourceElement sourceElement, final SourceRange sourceRange) { return new NonQualifiable<I>(identifierInfo, sourceElement, sourceRange); } /** * {@inheritDoc} * * @return the source element associated with the occurrence. Can *not* be null. */ @Override public SourceElement getSourceElement() { return sourceElement; } } /** * Represents a reference to a locally-bound entity. * * @author Joseph Wong */ public static final class Local<I extends IdentifierInfo.Local> extends Reference<I> { /** * The source element associated with the occurrence. Can *not* be null. * This should be either a * {@link org.openquark.cal.compiler.SourceModel.Name.Function SourceModel.Name.Function} or a * {@link org.openquark.cal.compiler.SourceModel.Name.Field SourceModel.Name.Field}. */ private final Name sourceElement; /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private Local(final I identifierInfo, final Name sourceElement, final SourceRange sourceRange) { super(identifierInfo, sourceRange); if (sourceElement == null) { throw new NullPointerException(); } this.sourceElement = sourceElement; } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @return an instance of this class. */ static <I extends IdentifierInfo.Local> Local<I> make(final I identifierInfo, final Name.Function sourceElement) { if (sourceElement.getModuleName() != null) { throw new IllegalArgumentException("The name should be unqualified"); } return new Local<I>(identifierInfo, sourceElement, sourceElement.getSourceRange()); } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @return an instance of this class. */ static <I extends IdentifierInfo.Local> Local<I> make(final I identifierInfo, final Name.Field sourceElement) { return new Local<I>(identifierInfo, sourceElement, sourceElement.getSourceRange()); } /** * {@inheritDoc} * * @return the source element associated with the occurrence. Can *not* be null. This should be either a * {@link org.openquark.cal.compiler.SourceModel.Name.Function SourceModel.Name.Function} or a * {@link org.openquark.cal.compiler.SourceModel.Name.Field SourceModel.Name.Field}. */ @Override public Name getSourceElement() { return sourceElement; } } /** * Abstract base class for an occurrence which is a reference to a data constructor field (including occurrences * of all associated data constructors). * * @author Joseph Wong */ public static abstract class DataConsFieldName extends Reference<IdentifierInfo.TopLevel.DataConsFieldName> { /** * Represents a data constructor field name not appearing in a punned context (including occurrences * of all associated data constructors). * * @author Joseph Wong */ public static final class NonPunned extends DataConsFieldName { /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param dataConsOccurrences the list of references for the data constructors associated with the field name. * Can *not* be null. The list should be non-empty. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private NonPunned( final IdentifierInfo.TopLevel.DataConsFieldName identifierInfo, final List<Reference.Qualifiable<IdentifierInfo.TopLevel.DataCons>> dataConsOccurrences, final Name.Field sourceElement, final SourceRange sourceRange) { super(identifierInfo, dataConsOccurrences, sourceElement, sourceRange); } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param dataConsOccurrences the list of references for the data constructors associated with the field name. * Can *not* be null. The list should be non-empty. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @return an instance of this class. */ static NonPunned make( final IdentifierInfo.TopLevel.DataConsFieldName identifierInfo, final List<Reference.Qualifiable<IdentifierInfo.TopLevel.DataCons>> dataConsOccurrences, final Name.Field sourceElement) { return new NonPunned(identifierInfo, dataConsOccurrences, sourceElement, sourceElement.getSourceRange()); } } /** * Represents a punned ordinal data constructor field name (including occurrences of all associated data constructors). * * @author Joseph Wong */ public static final class PunnedOrdinal extends DataConsFieldName { /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param dataConsOccurrences the list of references for the data constructors associated with the field name. * Can *not* be null. The list should be non-empty. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private PunnedOrdinal( final IdentifierInfo.TopLevel.DataConsFieldName identifierInfo, final List<Reference.Qualifiable<IdentifierInfo.TopLevel.DataCons>> dataConsOccurrences, final Name.Field sourceElement, final SourceRange sourceRange) { super(identifierInfo, dataConsOccurrences, sourceElement, sourceRange); } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param dataConsOccurrences the list of references for the data constructors associated with the field name. * Can *not* be null. The list should be non-empty. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @return an instance of this class. */ static PunnedOrdinal make( final IdentifierInfo.TopLevel.DataConsFieldName identifierInfo, final List<Reference.Qualifiable<IdentifierInfo.TopLevel.DataCons>> dataConsOccurrences, final Name.Field sourceElement) { if (!(sourceElement.getName() instanceof FieldName.Ordinal)) { throw new IllegalArgumentException("The field name is not an ordinal: " + sourceElement); } return new PunnedOrdinal(identifierInfo, dataConsOccurrences, sourceElement, sourceElement.getSourceRange()); } } /** * The list of references for the data constructors associated with the field name. Can *not* be null. * The list should be non-empty. If the list has more than one data constructor, the ordering should * following that of the case alternative. */ private final List<Reference.Qualifiable<IdentifierInfo.TopLevel.DataCons>> dataConsOccurrences; /** * The source element associated with the occurrence. Can *not* be null. */ private final Name.Field sourceElement; /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param dataConsOccurrences the list of references for the data constructors associated with the field name. * Can *not* be null. The list should be non-empty. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private DataConsFieldName( final IdentifierInfo.TopLevel.DataConsFieldName identifierInfo, final List<Reference.Qualifiable<IdentifierInfo.TopLevel.DataCons>> dataConsOccurrences, final Name.Field sourceElement, final SourceRange sourceRange) { super(identifierInfo, sourceRange); if (sourceElement == null || dataConsOccurrences == null) { throw new NullPointerException(); } this.sourceElement = sourceElement; this.dataConsOccurrences = Collections.unmodifiableList(new ArrayList<Reference.Qualifiable<IdentifierInfo.TopLevel.DataCons>>(dataConsOccurrences)); } /** * @return the list of references for the data constructors associated with the field name. Can *not* be null. * The list should be non-empty. If the list has more than one data constructor, the ordering should * following that of the case alternative. */ public List<Reference.Qualifiable<IdentifierInfo.TopLevel.DataCons>> getDataConsOccurrences() { return dataConsOccurrences; } /** * {@inheritDoc} * * @return the source element associated with the occurrence. Can *not* be null. */ @Override public Name.Field getSourceElement() { return sourceElement; } /** * {@inheritDoc} */ @Override public String toString() { return toStringWithCustomContent(" dataConsOccurrences=" + dataConsOccurrences); } } /** * Abstract base class for an occurrence which is a reference to a record field. * * @author Joseph Wong */ public static abstract class RecordFieldName extends Reference<IdentifierInfo.RecordFieldName> { /** * Represents a record field name not appearing in a punned context. * * @author Joseph Wong */ public static final class NonPunned extends RecordFieldName { /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private NonPunned(final IdentifierInfo.RecordFieldName identifierInfo, final Name.Field sourceElement, final SourceRange sourceRange) { super(identifierInfo, sourceElement, sourceRange); } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @return an instance of this class. */ static NonPunned make( final IdentifierInfo.RecordFieldName identifierInfo, final Name.Field sourceElement) { return new NonPunned(identifierInfo, sourceElement, sourceElement.getSourceRange()); } } /** * Represents a punned ordinal record field name. * * @author Joseph Wong */ public static final class PunnedOrdinal extends RecordFieldName { /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private PunnedOrdinal(final IdentifierInfo.RecordFieldName identifierInfo, final Name.Field sourceElement, final SourceRange sourceRange) { super(identifierInfo, sourceElement, sourceRange); } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @return an instance of this class. */ static PunnedOrdinal make( final IdentifierInfo.RecordFieldName identifierInfo, final Name.Field sourceElement) { if (!(sourceElement.getName() instanceof FieldName.Ordinal)) { throw new IllegalArgumentException("The field name is not an ordinal: " + sourceElement); } return new PunnedOrdinal(identifierInfo, sourceElement, sourceElement.getSourceRange()); } } /** * The source element associated with the occurrence. Can *not* be null. */ private final Name.Field sourceElement; /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private RecordFieldName(final IdentifierInfo.RecordFieldName identifierInfo, final Name.Field sourceElement, final SourceRange sourceRange) { super(identifierInfo, sourceRange); if (sourceElement == null) { throw new NullPointerException(); } this.sourceElement = sourceElement; } /** * {@inheritDoc} * * @return the source element associated with the occurrence. Can *not* be null. */ @Override public Name.Field getSourceElement() { return sourceElement; } } /** * Represents an operator reference (to a function, class method, type constructor, or data constructor). * * @author Joseph Wong */ public static final class Operator<I extends IdentifierInfo.TopLevel> extends Reference<I> { /** * Whether the reference appears in an expression context (i.e. the reference itself is an operator in an operator expression). */ private final boolean isExpressionContext; /** * The source element associated with the occurrence. Can *not* be null. */ private final SourceElement sourceElement; /** * Whether the operator consists of a pair of delimiters, e.g. (), []. */ private final boolean isDelimiterPair; /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param isExpressionContext whether the reference appears in an expression context (i.e. the reference itself is an operator in an operator expression). * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param isDelimiterPair whether the operator consists of a pair of delimiters, e.g. (), []. * @param operatorSourceRange the source range associated with the actual operator occurring in source. Can be null. */ private Operator(final I identifierInfo, final boolean isExpressionContext, final SourceElement sourceElement, final boolean isDelimiterPair, final SourceRange operatorSourceRange) { super(identifierInfo, operatorSourceRange); if (sourceElement == null) { throw new NullPointerException(); } this.isExpressionContext = isExpressionContext; this.sourceElement = sourceElement; this.isDelimiterPair = isDelimiterPair; } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param isExpressionContext whether the reference appears in an expression context (i.e. the reference itself is an operator in an operator expression). * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param isDelimiterPair whether the operator consists of a pair of delimiters, e.g. (), []. * @param operatorSourceRange the source range associated with the actual operator occurring in source. Can be null. * @return an instance of this class. */ static <I extends IdentifierInfo.TopLevel> Operator<I> make( final I identifierInfo, final boolean isExpressionContext, final SourceElement sourceElement, final boolean isDelimiterPair, final SourceRange operatorSourceRange) { return new Operator<I>(identifierInfo, isExpressionContext, sourceElement, isDelimiterPair, operatorSourceRange); } /** * @return whether the reference appears in an expression context (i.e. the reference itself is an operator in an operator expression). */ public boolean isExpressionContext() { return isExpressionContext; } /** * {@inheritDoc} * * @return the source element associated with the occurrence. Can *not* be null. */ @Override public SourceElement getSourceElement() { return sourceElement; } /** * @return wWhether the operator consists of a pair of delimiters, e.g. (), []. */ public boolean isDelimiterPair() { return isDelimiterPair; } /** * {@inheritDoc} */ @Override public String toString() { return toStringWithCustomContent(" isExpressionContext=" + isExpressionContext + " isDelimiterPair=" + isDelimiterPair); } } /** * Represents a reference to a type variable. * * @author Joseph Wong */ public static final class TypeVariable extends Reference<IdentifierInfo.TypeVariable> { /** * The source element associated with the occurrence. Can *not* be null. */ private final Name.TypeVar sourceElement; /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private TypeVariable(final IdentifierInfo.TypeVariable identifierInfo, final Name.TypeVar sourceElement, final SourceRange sourceRange) { super(identifierInfo, sourceRange); if (sourceElement == null) { throw new NullPointerException(); } this.sourceElement = sourceElement; } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @return an instance of this class. */ static TypeVariable make(final IdentifierInfo.TypeVariable identifierInfo, final Name.TypeVar sourceElement) { return new TypeVariable(identifierInfo, sourceElement, sourceElement.getSourceRange()); } /** * {@inheritDoc} * * @return the source element associated with the occurrence. Can *not* be null. */ @Override public SourceElement getSourceElement() { return sourceElement; } } /** * Private constructor. Intended for subclasses only. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceRange the source range associated with the actual name occurring in source. Can be null. */ private Reference(final I identifierInfo, final SourceRange sourceRange) { super(identifierInfo, sourceRange); } } /** * Represents a foreign descriptor (or external name) for a foreign entity. * * @author Joseph Wong */ public static final class ForeignDescriptor<I extends IdentifierInfo.TopLevel> extends IdentifierOccurrence<I> { /** * The source element associated with the occurrence. Can *not* be null. */ private final SourceElement sourceElement; /** * Constructs an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param foreignDescriptorSourceRange the source range associated with the actual foreign descriptor occurring in source. Can be null. */ private ForeignDescriptor(final I identifierInfo, final SourceElement sourceElement, final SourceRange foreignDescriptorSourceRange) { super(identifierInfo, foreignDescriptorSourceRange); if (sourceElement == null) { throw new NullPointerException(); } this.sourceElement = sourceElement; } /** * Factory method for constructing an instance of this identifier occurrence. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceElement the source element associated with the occurrence. Can *not* be null. * @param foreignDescriptorSourceRange the source range associated with the actual foreign descriptor occurring in source. Can be null. * @return an instance of this class. */ static <I extends IdentifierInfo.TopLevel> ForeignDescriptor<I> make( final I identifierInfo, final SourceElement sourceElement, final SourceRange foreignDescriptorSourceRange) { return new ForeignDescriptor<I>(identifierInfo, sourceElement, foreignDescriptorSourceRange); } /** * {@inheritDoc} * * @return the source element associated with the occurrence. Can *not* be null. */ @Override public SourceElement getSourceElement() { return sourceElement; } } /** * A {@link Comparator} implementation for {@link IdentifierOccurrence}s based on source positions. * * @author Joseph Wong */ private static final class SourcePositionBasedComparator implements Comparator<IdentifierOccurrence<?>> { public int compare(final IdentifierOccurrence<?> a, final IdentifierOccurrence<?> b) { final SourceRange aRange = a.getSourceRange(); final SourceRange bRange = b.getSourceRange(); // null < non-null if (aRange == null) { if (bRange == null) { return 0; } else { return -1; } } else { if (bRange == null) { return 1; } else { // first by start position, then by end position final int startPosComparison = SourcePosition.compareByPosition.compare( aRange.getStartSourcePosition(), bRange.getStartSourcePosition()); if (startPosComparison != 0) { return startPosComparison; } else { return SourcePosition.compareByPosition.compare( aRange.getEndSourcePosition(), bRange.getEndSourcePosition()); } } } } } /** * A {@link Comparator} singleton for {@link IdentifierOccurrence}s based on source positions. */ public static final Comparator<IdentifierOccurrence<?>> SOURCE_POSITION_BASED_COMPARATOR = new SourcePositionBasedComparator(); /** * The identity of the CAL entity referred to by the identifier. * * Can *not* be null. */ private final I identifierInfo; /** * The source range associated with the precise reference occurring in source. * * Can be null. */ private final SourceRange sourceRange; /** * Private constructor. Intended for subclasses only. * @param identifierInfo the identity of the CAL entity referred to by the identifier. Can *not* be null. * @param sourceRange the source range associated with the precise reference occurring in source. Can be null. */ private IdentifierOccurrence(final I identifierInfo, final SourceRange sourceRange) { if (identifierInfo == null) { throw new NullPointerException(); } this.identifierInfo = identifierInfo; this.sourceRange = sourceRange; } /** * @return the identity of the CAL entity referred to by the identifier. Can *not* be null. */ public final I getIdentifierInfo() { return identifierInfo; } /** * @return the source element associated with the occurrence. This may be more than just a name, * but can be the entire definition of an entity. Can be null. */ public abstract SourceElement getSourceElement(); /** * @return the source range associated with the precise reference occurring in source. Can be null. */ public final SourceRange getSourceRange() { return sourceRange; } /** * {@inheritDoc} */ @Override public String toString() { return toStringWithCustomContent(""); } /** * Produces a string for {@link #toString()} containing standard content, and with the provided custom content. * @param customContent the custom contented to be included. * @return a string representing this instance. */ String toStringWithCustomContent(final String customContent) { return "{" + getShortClassName(this.getClass()) + " identifier=" + getIdentifierInfo() + customContent + " sourceElement::" + getShortClassName(getSourceElement().getClass()) + " sourceRange=" + getSourceRange() + "}"; } /** * Produces a short name of a class, e.g. IdentifierOccurrence.Reference.Module is a short name. * @param clazz the class, which must be in a package. * @return a short name for the class. */ private static final String getShortClassName(final Class<?> clazz) { return clazz.getName().substring(clazz.getPackage().getName().length() + 1).replace('$', '.'); } }