/* * 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. */ /* * ParseTreeNode.java * Creation date: (Dec 5, 2000) * By: Bo Ilic */ package org.openquark.cal.compiler; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.math.BigInteger; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.SortedSet; import org.openquark.cal.compiler.Expression.ErrorInfo; import org.openquark.util.EmptyIterator; import org.openquark.util.UnsafeCast; import antlr.Token; import antlr.collections.AST; /** * Warning- this class should only be used by the CAL compiler implementation. It is not part of the * external API of the CAL platform. * <P> * ParseTreeNode is the class used for nodes in the abstract syntax tree within CAL. It augments * antlr's default abstract syntax tree node class, CommonAST with positional information to help * with error reporting during type checking. * Creation date: (Dec 5, 2000) * @author Bo Ilic */ public final class ParseTreeNode extends antlr.CommonAST implements Iterable<ParseTreeNode> { private static final long serialVersionUID = -301432238203275995L; /** identifies the range in the CAL source stream that this node occupies. May be null. */ private SourceRange sourceRange; /** * The map containing custom attribute key-value pairs associated with this node. * * It is not recommended that additional fields be added to this class unless the fields are * applicable to all parse tree nodes. Instead, this attribute mechanism should be used instead * to associate additional data with nodes of particular types. */ private Map<AttributeKey, Object> attributes; /** * A typesafe enumeration of the attribute keys that can be used with the attributes map. * * @author Joseph Wong */ private static final class AttributeKey { /** * The key for the attribute storing the module name as appearing in the source. * The node itself may contain a fully-qualified module name introduced by the name resolution process. */ private static final AttributeKey MODULE_NAME_IN_SOURCE = new AttributeKey("ModuleNameInSource"); /** * The key for the attribute storing the function type for a local function's CALDoc * comment, which is subsequently retrieved in the process of verifying the validity of * the CALDoc comment. */ private static final AttributeKey FUNCTION_TYPE_FOR_LOCAL_FUNCTION_CALDOC_COMMENT = new AttributeKey("FunctionTypeForLocalFunctionCALDocComment"); /** * The key for the attribute storing the QualifiedName of the data constructor as which * the CALDoc cross reference can be resolved. (The cross reference would be the kind that lacks the 'context' keyword, * e.g. {at-link Nothing at-}.) */ private static final AttributeKey CALDOC_CROSS_REFERENCE_RESOLVED_AS_DATA_CONSTRUCTOR = new AttributeKey("CALDocCrossReferenceResolvedAsDataConstructor"); /** * The key for the attribute storing the category of a CALDoc cross reference that consists of an uppercase identifier * and that lacks an associated 'context' keyword, e.g. {at-link Nothing at-}. */ private static final AttributeKey CATEGORY_FOR_CALDOC_CONS_NAME_WITHOUT_CONTEXT_CROSS_REFERENCE = new AttributeKey("CategoryForCALDocConsNameWithoutContextCrossReference"); /** * The key for the attribute storing the ErrorInfo object for a node representing an error call. */ private static final AttributeKey ERROR_INFO_FOR_ERROR_CALL = new AttributeKey("ErrorInfoForErrorCall"); /** * The key for the attribute storing the unused variable name for a wildcard pattern variable (i.e. the underscore). */ private static final AttributeKey UNUSED_VAR_NAME_FOR_WILDCARD_PATTERN_VAR = new AttributeKey("UnusedVarNameForWildcardPatternVar"); /** * The key for the attribute storing a (Byte/Short/Integer/Long) value for an * INTEGER_LITERAL node or a MINUS node representing a negated integer. */ private static final AttributeKey LITERAL_VALUE_FOR_MAYBE_MINUS_INT_LITERAL = new AttributeKey("LiteralValueForMaybeMinusIntLiteral"); /** * The key for the attribute storing a (Float/Double) value for a FLOAT_LITERAL node. */ private static final AttributeKey LITERAL_VALUE_FOR_FLOAT_LITERAL = new AttributeKey("LiteralValueForFloatLiteral"); /** * The key for the attribute storing a Character value for a CHAR_LITERAL node. */ private static final AttributeKey CHARACTER_VALUE_FOR_CHAR_LITERAL = new AttributeKey("CharacterValueForCharLiteral"); /** * The key for the attribute to mark this node as internally generated, as opposed to read from source * this is used to mark function etc that are generated by the deriving clause etc. */ private static final AttributeKey IS_INTERNALLY_GENERATED = new AttributeKey("IsInternallyGenerated"); /** * The key for the attribute storing a TypeExpr for a FUNCTION_PARAM_LIST node. */ private static final AttributeKey TYPE_EXPR_FOR_FUNCTION_PARAM_LIST = new AttributeKey("TypeExprForFunctionParamList"); /** * The key for the attribute marking a parameter as being a lifed argument or not. */ private static final AttributeKey IS_LIFTED_ARGUMENT = new AttributeKey("IsLiftedArgument"); /** * The key for the attribute storing a TypeExpr for a case expression node (VIRTUAL_DATA_CONSTRUCTOR_CASE, * VIRTUAL_RECORD_CASE, VIRTUAL_TUPLE_CASE). */ private static final AttributeKey TYPE_EXPR_FOR_CASE_EXPR = new AttributeKey("TypeExprForCaseExpr"); /** * The key for the attribute storing a boolean flag marking a node as synthetic with a VAR_ID or SELECT_RECORD_FIELD node. * <p> * For example: * <pre> * let Prelude.Cons {head, tail=xs} = foo; in ... * </pre> * is desugared into: * <pre> * let * $pattern_head_xs = foo; * head = $pattern_head_xs.Prelude.Cons.head; * xs = $pattern_head_xs.Prelude.Cons.tail; * in ... * </pre> * And all occurences of the VAR_ID <code>$pattern_head_xs</code> will be marked with this attribute set to true. * <p> * For desugared record and tuple patterns, it is the SELECT_RECORD_FIELD node that will be marked. */ private static final AttributeKey IS_SYNTHETIC_VAR_OR_RECORD_FIELD_SELECTION = new AttributeKey("IsSyntheticVarOrRecordFieldSelection"); /** * The key for the attribute storing a boolean flag marking a LET_DEFN node as being a desugared definition for a local pattern match declaration. * <p> * For example: * <pre> * let Prelude.Cons {head, tail=xs} = foo; in ... * </pre> * is desugared into: * <pre> * let * $pattern_head_xs = foo; * head = $pattern_head_xs.Prelude.Cons.head; * xs = $pattern_head_xs.Prelude.Cons.tail; * in ... * </pre> * And each of the desugared definitions (for <code>$pattern_head_xs</code>, <code>head</code> and <code>xs</code>) will be marked with this attribute set to true. */ private static final AttributeKey IS_DESUGARED_PATTERN_MATCH_FOR_LET_DEFN = new AttributeKey("IsDesugaredPatternMatchForLetDefn"); /** * The key for the attribute storing a SortedSet of {@link FieldName}s corresponding to non-polymorphic record patterns appearing in a local pattern match declaration. * <p> * For example: * <pre> * let {#3=x, y, z=alpha} = foo; in ... * </pre> * is desugared into: * <pre> * let * $pattern_x_y_alpha = foo; * x = $pattern_x_y_alpha.#3; * y = $pattern_x_y_alpha.y; * alpha = $pattern_x_y_alpha.z; * in ... * </pre> * And the LET_DEFN node for <code>$pattern_head_xs</code> is associated with the set of the declared field names {#3, y, z}. */ private static final AttributeKey DECLARED_FIELDS_IN_NON_POLYMORPHIC_RECORD_PATTERN_MATCH_FOR_LET_DEFN = new AttributeKey("DeclaredFieldsInNonPolymorphicRecordPatternMatchForLetDefn"); /** * The key for the attribute storing a SortedSet of {@link FieldName}s corresponding to polymorphic record patterns appearing in a local pattern match declaration. * <p> * For example: * <pre> * let {_ | #8, #9, #3=x, y, z=alpha} = foo; in ... * </pre> * is desugared into: * <pre> * let * $pattern_x_y_alpha = foo; * x = $pattern_x_y_alpha.#3; * y = $pattern_x_y_alpha.y; * alpha = $pattern_x_y_alpha.z; * in ... * </pre> * And the LET_DEFN node for <code>$pattern_head_xs</code> is associated with the set of the declared field names {#3, #8, #9, y, z}. */ private static final AttributeKey DECLARED_FIELDS_IN_POLYMORPHIC_RECORD_PATTERN_MATCH_FOR_LET_DEFN = new AttributeKey("DeclaredFieldsInNonPolymorphicRecordPatternMatchForLetDefn"); /** The display name of the attribute key. */ private final String displayName; /** * Private constructor. * @param displayName the display name of the attribute key. */ private AttributeKey(String displayName) { this.displayName = displayName; } /** @return the display name of the attribute key. */ @Override public String toString() { return displayName; } } /** * An Iterator over the children of a ParseTreeNode so that ParseTreeNode can be used * with the for-each loops in Java 5. * Does not support modification while doing the iteration of the number and order of the children * of the constructor ParseTreeNode, although the children itself may have their children modified. * @author Bo Ilic */ static private class ParseTreeNodeIterator implements Iterator<ParseTreeNode> { private ParseTreeNode nextNode; private ParseTreeNodeIterator(final ParseTreeNode nextNode) { this.nextNode = nextNode; } /** * Makes an iterator over the children of the argument parseTreeNode. */ static Iterator<ParseTreeNode> make(final ParseTreeNode parseTreeNode) { final ParseTreeNode nextNode = parseTreeNode.firstChild(); if (nextNode == null) { return EmptyIterator.<ParseTreeNode>emptyIterator(); } return new ParseTreeNodeIterator(nextNode); } /** * Makes an iterator over the subsequent siblings of the argument parseTreeNode * (not including the parseTreeNode itself). */ static Iterator<ParseTreeNode> nextSiblings(final ParseTreeNode parseTreeNode) { final ParseTreeNode nextNode = parseTreeNode.nextSibling(); if (nextNode == null) { return EmptyIterator.<ParseTreeNode>emptyIterator(); } return new ParseTreeNodeIterator(nextNode); } /** {@inheritDoc} */ public boolean hasNext() { return nextNode != null; } /** {@inheritDoc} */ public ParseTreeNode next() { if (nextNode == null) { throw new NoSuchElementException(); } final ParseTreeNode returnNode = nextNode; nextNode = nextNode.nextSibling(); return returnNode; } /** {@inheritDoc} */ public void remove() { throw new UnsupportedOperationException(); } } /** * ASTNode constructor. * Antlr invokes ParseTreeNode constructors via reflection, so must be public scope. */ public ParseTreeNode() { super(); } /** * Constructs a ParseTreeNode from a tokenType and node text. * Antlr invokes ParseTreeNode constructors via reflection, so must be public scope. * * * @param tokenType the token type as in CALTreeParserTokenTypes * @param text textual name associated with the node */ public ParseTreeNode(int tokenType, String text) { this(tokenType, text, null); } /** * Constructs a ParseTreeNode from a tokenType, node text and SourcePosition. * Antlr invokes ParseTreeNode constructors via reflection, so must be public scope. * * Creation date: (4/17/01 9:08:09 AM) * @param tokenType * @param text * @param sourcePosition */ public ParseTreeNode(int tokenType, String text, SourcePosition sourcePosition) { super(); if(sourcePosition == null) { sourceRange = null; } else if(text == null) { sourceRange = new SourceRange(sourcePosition, sourcePosition); } else { sourceRange = new SourceRange(sourcePosition, text); } initialize(tokenType, text); } /** * ParseTreeNode constructor. * Antlr invokes ParseTreeNode constructors via reflection, so must be public scope. * * @param arg1 token from which to create a node */ public ParseTreeNode(Token arg1) { super(arg1); } /** * A helper function that creates the parseTree for a qualifiedVar expression. * Creation date: (5/31/01 7:43:14 AM) * @param varName * @param sourcePosition * @return ParseTreeNode */ static ParseTreeNode makeQualifiedVarNode(QualifiedName varName, SourcePosition sourcePosition) { return makeQualifiedVarNode(varName.getModuleName(), varName.getUnqualifiedName(), sourcePosition); } /** * A helper function that creates the parseTree for a qualifiedVar expression. * @param moduleName * @param varName * @param sourcePosition * @return ParseTreeNode */ static ParseTreeNode makeQualifiedVarNode(ModuleName moduleName, String varName, SourcePosition sourcePosition) { return makeQualifiedVarNode(moduleName.toSourceText(), varName, sourcePosition); } /** * A helper function that creates the parseTree for an unqualified qualifiedVar expression. * @param varName * @param sourcePosition * @return ParseTreeNode */ static ParseTreeNode makeUnqualifiedVarNode(String varName, SourcePosition sourcePosition) { return makeQualifiedVarNode("", varName, sourcePosition); } /** * A helper function that creates the parseTree for a qualifiedVar expression. * @param moduleName * @param varName * @param sourcePosition * @return ParseTreeNode */ private static ParseTreeNode makeQualifiedVarNode(String moduleName, String varName, SourcePosition sourcePosition) { ParseTreeNode qualifiedVarNode = new ParseTreeNode(CALTreeParserTokenTypes.QUALIFIED_VAR, "QUALIFIED_VAR", sourcePosition); ParseTreeNode moduleNameNode = ModuleNameUtilities.makeParseTreeForMaybeModuleName(moduleName.toString(), sourcePosition); qualifiedVarNode.setFirstChild(moduleNameNode); ParseTreeNode varNameNode = new ParseTreeNode(CALTreeParserTokenTypes.VAR_ID, varName, sourcePosition); moduleNameNode.setNextSibling(varNameNode); return qualifiedVarNode; } /** * A helper function that creates the parseTree for a qualifiedCons expression. * @param qualifiedConsName the qualified name. * @param sourcePosition the associated source position. * @return a new ParseTreeNode representing the qualifiedCons expression. */ static ParseTreeNode makeQualifiedConsNode(QualifiedName qualifiedConsName, SourcePosition sourcePosition) { ParseTreeNode qualifiedConsNode = new ParseTreeNode(CALTreeParserTokenTypes.QUALIFIED_CONS, "QUALIFIED_CONS", sourcePosition); ParseTreeNode moduleNameNode = ModuleNameUtilities.makeParseTreeForMaybeModuleName(qualifiedConsName.getModuleName().toSourceText(), sourcePosition); qualifiedConsNode.setFirstChild(moduleNameNode); ParseTreeNode varNameNode = new ParseTreeNode(CALTreeParserTokenTypes.CONS_ID, qualifiedConsName.getUnqualifiedName(), sourcePosition); moduleNameNode.setNextSibling(varNameNode); return qualifiedConsNode; } /** * Creates a ParseTreeNode representing an integer literal. * @param intValue the int value to be represented as an integer literal ParseTreeNode. * @return a new ParseTreeNode representing an integer literal. */ static ParseTreeNode makeIntLiteralNodeWithIntegerValue(int intValue) { ParseTreeNode intLiteralNode = new ParseTreeNode(CALTreeParserTokenTypes.INTEGER_LITERAL, String.valueOf(intValue)); intLiteralNode.setIntegerValueForMaybeMinusIntLiteral(Integer.valueOf(intValue)); return intLiteralNode; } /** * Set all the fields from another parse tree node. However, pointers to this node * are still retained. * Creation date: (4/19/01 4:32:46 PM) * @param node */ void copyContentsFrom(ParseTreeNode node) { setType(node.getType()); setText(node.getText()); setFirstChild(node.getFirstChild()); setNextSibling(node.getNextSibling()); sourceRange = node.sourceRange; attributes = node.attributes; } /** * @return a deep copy of the parse tree rooted at this node. */ ParseTreeNode copyParseTree() { ParseTreeNode parseTreeCopy = new ParseTreeNode(getType(), getText(), getSourcePosition()); parseTreeCopy.attributes = makeAttributesCopyFrom(attributes); ParseTreeNode firstChild = firstChild(); if (firstChild != null) { parseTreeCopy.setFirstChild(firstChild.copyParseTree()); } ParseTreeNode nextSibling = nextSibling(); if (nextSibling != null) { parseTreeCopy.setNextSibling(nextSibling.copyParseTree()); } return parseTreeCopy; } /** * @return the first child of this node. */ ParseTreeNode firstChild() { return (ParseTreeNode)getFirstChild(); } /** * @return the next sibling in line after this one. */ ParseTreeNode nextSibling() { return (ParseTreeNode)getNextSibling(); } /** * This is more efficient than calling getNChildren and checking that it is == 0. * @return true if this ParseTreeNode has 0 children. */ boolean hasNoChildren() { return firstChild() == null; } /** * This is more efficient than calling getNChildren and checking that it is == 1. * @return true if this ParseTreeNode has exactly 1 child. */ boolean hasExactlyOneChild() { ParseTreeNode firstChild = firstChild(); if (firstChild == null) { return false; } return firstChild.nextSibling() == null; } /** * Get the child of this ParseTreeNode with the given 0-based index. * Creation date: (5/29/01 3:05:03 PM) * @return ParseTreeNode * @param zeroBasedChildN 0-based index to the child of this ParseTreeNode */ ParseTreeNode getChild(int zeroBasedChildN) { AST childNode = getFirstChild(); for (int i = 0; i < zeroBasedChildN; ++i) { childNode = childNode.getNextSibling(); } return (ParseTreeNode) childNode; } /** * @return the last child of this ParseTreeNode, or null if the node has no children. */ ParseTreeNode lastChild() { AST lastChild = getFirstChild(); if (lastChild == null) { return null; } while (true) { AST nextSibling = lastChild.getNextSibling(); if (nextSibling == null) { return (ParseTreeNode)lastChild; } lastChild = nextSibling; } } /** * Equivalent to calling addChild sequentially over the children, but more efficient since repeated * traversals of the children are avoided. * @param children */ void addChildren(ParseTreeNode[] children) { int nChildrenToAdd = children.length; if (nChildrenToAdd == 0) { return; } ParseTreeNode lastChild = lastChild(); if (lastChild == null) { //the case where this ParseTreeNode has no children ParseTreeNode currentNode = children[0]; setFirstChild(currentNode); for (int i = 1; i < nChildrenToAdd; ++i) { ParseTreeNode nextNode = children[i]; currentNode.setNextSibling(nextNode); currentNode = nextNode; } } else { //if there are already children, append to the end. ParseTreeNode currentNode = lastChild; for (int i = 0; i < nChildrenToAdd; ++i) { ParseTreeNode nextNode = children[i]; currentNode.setNextSibling(nextNode); currentNode = nextNode; } } } /** * @return the CAL source position corresponding to the AST node (could be null, if this can't be traced). * @see #getAssemblySourcePosition */ SourcePosition getSourcePosition() { if(sourceRange != null) { return sourceRange.getStartSourcePosition(); } else { return null; } } /** * @return the source range corresponding to the AST node (may be null). * @see #getAssemblySourceRange */ SourceRange getSourceRange() { return sourceRange; } /** * Returns the source position of the node, if it is non-null, otherwise, recursively searches child nodes * for a non-null source position, and returns it. The idea here is that in general nodes not corresponding * directly to source tokens will have null sourcePositions, but they are essentially assemblies of nodes, * so that the first non-null sourcePosition encountered in a child can be thought of as the position * in the CAL source of the assembly. * @return the CAL source position corresponding to the assembly (could be null, if this can't be traced). * @see #getSourcePosition */ SourcePosition getAssemblySourcePosition() { if (sourceRange != null) { return sourceRange.getStartSourcePosition(); } for (final ParseTreeNode node : this) { SourcePosition pos = node.getAssemblySourcePosition(); if (pos != null) { return pos; } } return null; } /** * Returns the SourceRange of the node, if it is non-null, otherwise recursively searches child nodes * for non-null source ranges, and returns a new range consisting of the leftmost start position to * the rightmost end position. */ SourceRange getAssemblySourceRange() { SourcePosition leftEdge = null; SourcePosition rightEdge = null; if(sourceRange != null) { leftEdge = sourceRange.getStartSourcePosition(); rightEdge = sourceRange.getEndSourcePosition(); } for(ParseTreeNode node = firstChild(); node != null; node = node.nextSibling()) { SourceRange range = node.getAssemblySourceRange(); if(range != null) { if(leftEdge == null || SourcePosition.compareByPosition.compare(range.getStartSourcePosition(), leftEdge) < 0) { leftEdge = range.getStartSourcePosition(); } if(rightEdge == null || SourcePosition.compareByPosition.compare(range.getEndSourcePosition(), rightEdge) > 0) { rightEdge = range.getEndSourcePosition(); } } } if(leftEdge != null && rightEdge != null) { return new SourceRange(leftEdge, rightEdge); } else { return null; } } /** * Associates a TypeExpr value with a FUNCTION_PARAM_LIST node, storing it in the attributes map. * @param typeExpr the TypeExpr value. */ void setTypeExprForFunctionParamList(TypeExpr typeExpr) { verifyType(CALTreeParserTokenTypes.FUNCTION_PARAM_LIST); putAttributeValue(AttributeKey.TYPE_EXPR_FOR_FUNCTION_PARAM_LIST, typeExpr); } /** * Marks this parse tree node (and children) as internally generated * @param range in the source code which caused the nodes to be generated, or null. */ void setInternallyGenerated(SourceRange range) { putAttributeValue(AttributeKey.IS_INTERNALLY_GENERATED, Boolean.TRUE); if (range != null) { this.sourceRange = range; } for (final ParseTreeNode child : this) { child.setInternallyGenerated(range); } } /** * @return true if the parse node was internally generated. */ boolean isInternallyGenerated() { return (Boolean)getAttributeValue(AttributeKey.IS_INTERNALLY_GENERATED) == Boolean.TRUE; } /** * Retrieves the associated TypeExpr value of a FUNCTION_PARAM_LIST node, fetching it from the attributes map. * @return the associated TypeExpr value. */ TypeExpr getTypeExprForFunctionParamList() { verifyType(CALTreeParserTokenTypes.FUNCTION_PARAM_LIST); return (TypeExpr)getAttributeValue(AttributeKey.TYPE_EXPR_FOR_FUNCTION_PARAM_LIST); } /** * Marks the parameter as being a lifed argument or not. * @param isLifted The flag indicating if the argument is lifted. */ void setIsLiftedArgument(boolean isLifted) { verifyType(CALTreeParserTokenTypes.LAZY_PARAM, CALTreeParserTokenTypes.STRICT_PARAM); putAttributeValue(AttributeKey.IS_LIFTED_ARGUMENT, Boolean.valueOf(isLifted)); } /** * Retrieves the flag indicating if the parameter is was lifted. * @return the associated TypeExpr value. */ boolean getIsLiftedArgument() { verifyType(CALTreeParserTokenTypes.LAZY_PARAM, CALTreeParserTokenTypes.STRICT_PARAM); Boolean isLiftedArgument = (Boolean) getAttributeValue(AttributeKey.IS_LIFTED_ARGUMENT); if (isLiftedArgument == null){ return false; } else{ return isLiftedArgument.booleanValue(); } } /** * Associates a TypeExpr value with a case expression node (VIRTUAL_DATA_CONSTRUCTOR_CASE, VIRTUAL_RECORD_CASE, VIRTUAL_TUPLE_CASE), * storing it in the attributes map. * @param typeExpr the TypeExpr value. */ void setTypeExprForCaseExpr(TypeExpr typeExpr) { verifyType(CALTreeParserTokenTypes.VIRTUAL_DATA_CONSTRUCTOR_CASE, CALTreeParserTokenTypes.VIRTUAL_RECORD_CASE, CALTreeParserTokenTypes.VIRTUAL_TUPLE_CASE); putAttributeValue(AttributeKey.TYPE_EXPR_FOR_CASE_EXPR, typeExpr); } /** * Retrieves the associated TypeExpr value of a case expression node (VIRTUAL_DATA_CONSTRUCTOR_CASE, VIRTUAL_RECORD_CASE, VIRTUAL_TUPLE_CASE), * fetching it from the attributes map. * @return the associated TypeExpr value. */ TypeExpr getTypeExprForCaseExpr() { verifyType(CALTreeParserTokenTypes.VIRTUAL_DATA_CONSTRUCTOR_CASE, CALTreeParserTokenTypes.VIRTUAL_RECORD_CASE, CALTreeParserTokenTypes.VIRTUAL_TUPLE_CASE); return (TypeExpr)getAttributeValue(AttributeKey.TYPE_EXPR_FOR_CASE_EXPR); } /** * Sets the module name as appearing in the source. This node may contain a fully-qualified module name introduced by the name resolution process. * @param moduleName the module name as appearing in source. */ void setModuleNameInSource(String moduleName) { verifyType(CALTreeParserTokenTypes.HIERARCHICAL_MODULE_NAME, CALTreeParserTokenTypes.HIERARCHICAL_MODULE_NAME_EMPTY_QUALIFIER); putAttributeValue(AttributeKey.MODULE_NAME_IN_SOURCE, moduleName); } /** * Returns the module name as appearing in the source. This node may contain a fully-qualified module name introduced by the name resolution process. * @return the module name as appearing in source.the module name as appearing in source, or null if this node is not modified by the name resolution process. */ String getModuleNameInSource() { verifyType(CALTreeParserTokenTypes.HIERARCHICAL_MODULE_NAME, CALTreeParserTokenTypes.HIERARCHICAL_MODULE_NAME_EMPTY_QUALIFIER); return (String)getAttributeValue(AttributeKey.MODULE_NAME_IN_SOURCE); } /** * Associates the type of a local function with an OPTIONAL_CALDOC_COMMENT node representing the CALDoc comment * for that local function, which is subsequently retrieved in the process of verifying the validity of * the CALDoc comment. * @param localFunctionType the type of the local function. */ void setFunctionTypeForLocalFunctionCALDocComment(TypeExpr localFunctionType) { verifyType(CALTreeParserTokenTypes.OPTIONAL_CALDOC_COMMENT); putAttributeValue(AttributeKey.FUNCTION_TYPE_FOR_LOCAL_FUNCTION_CALDOC_COMMENT, localFunctionType); } /** * Retrieves the type of a local function associated with an OPTIONAL_CALDOC_COMMENT node representing the CALDoc comment * for that local function, for use in the process of verifying the validity of the CALDoc comment. * @return the type of the local function. */ TypeExpr getFunctionTypeForLocalFunctionCALDocComment() { verifyType(CALTreeParserTokenTypes.OPTIONAL_CALDOC_COMMENT); return (TypeExpr)getAttributeValue(AttributeKey.FUNCTION_TYPE_FOR_LOCAL_FUNCTION_CALDOC_COMMENT); } /** * Associates the QualifiedName of the data constructor which this CALDoc cross reference (appearing without a * 'context' keyword) resolves to. * @param dataConsName the QualifiedName of the data constructor to which this reference resolves. */ void setCALDocCrossReferenceResolvedAsDataConstructor(QualifiedName dataConsName) { verifyType(CALTreeParserTokenTypes.CALDOC_CHECKED_QUALIFIED_CONS, CALTreeParserTokenTypes.CALDOC_UNCHECKED_QUALIFIED_CONS); putAttributeValue(AttributeKey.CALDOC_CROSS_REFERENCE_RESOLVED_AS_DATA_CONSTRUCTOR, dataConsName); } /** * Retrieves the QualifiedName of the data constructor which this CALDoc cross reference (appearing without a * 'context' keyword) resolves to. * @return the QualifiedName of the data constructor to which this reference resolves, or null if the reference * was not processed by a name resolution pass or could not be unambiguously resolved by such a pass. */ QualifiedName getCALDocCrossReferenceResolvedAsDataConstructor() { verifyType(CALTreeParserTokenTypes.CALDOC_CHECKED_QUALIFIED_CONS, CALTreeParserTokenTypes.CALDOC_UNCHECKED_QUALIFIED_CONS); return (QualifiedName)getAttributeValue(AttributeKey.CALDOC_CROSS_REFERENCE_RESOLVED_AS_DATA_CONSTRUCTOR); } /** * Associates a category of a CALDoc cross reference that consists of an uppercase identifier * and that lacks an associated 'context' keyword, e.g. {at-link Nothing at-}. * @param category the resolved category of the cross reference. */ void setCategoryForCALDocConsNameWithoutContextCrossReference(CALDocChecker.CategoryForCALDocConsNameWithoutContextCrossReference category) { verifyType(CALTreeParserTokenTypes.CALDOC_CHECKED_QUALIFIED_CONS, CALTreeParserTokenTypes.CALDOC_UNCHECKED_QUALIFIED_CONS); putAttributeValue(AttributeKey.CATEGORY_FOR_CALDOC_CONS_NAME_WITHOUT_CONTEXT_CROSS_REFERENCE, category); } /** * Retrieves the associated category of a CALDoc cross reference that consists of an uppercase identifier * and that lacks an associated 'context' keyword, e.g. {at-link Nothing at-}. * @return the resolved category of the cross reference, or null if the reference was not processed by a name resolution * pass or could not be unambiguously resolved by such a pass. */ CALDocChecker.CategoryForCALDocConsNameWithoutContextCrossReference getCategoryForCALDocConsNameWithoutContextCrossReference() { verifyType(CALTreeParserTokenTypes.CALDOC_CHECKED_QUALIFIED_CONS, CALTreeParserTokenTypes.CALDOC_UNCHECKED_QUALIFIED_CONS); return (CALDocChecker.CategoryForCALDocConsNameWithoutContextCrossReference)getAttributeValue(AttributeKey.CATEGORY_FOR_CALDOC_CONS_NAME_WITHOUT_CONTEXT_CROSS_REFERENCE); } /** * Associates an ErrorInfo object with a node representing an error call, storing it in the attributes map. * @param errorInfo the object containing information about the error call. */ void setErrorInfoForErrorCall(ErrorInfo errorInfo) { verifyType( CALTreeParserTokenTypes.QUALIFIED_VAR, CALTreeParserTokenTypes.SELECT_DATA_CONSTRUCTOR_FIELD, CALTreeParserTokenTypes.VIRTUAL_TUPLE_CASE, CALTreeParserTokenTypes.VIRTUAL_RECORD_CASE, CALTreeParserTokenTypes.VIRTUAL_DATA_CONSTRUCTOR_CASE); putAttributeValue(AttributeKey.ERROR_INFO_FOR_ERROR_CALL, errorInfo); } /** * Retrieves the ErrorInfo object associated with a node representing an error call, fetching it from the attributes map. * @return the object containing information about the error call. */ ErrorInfo getErrorInfoForErrorCall() { verifyType( CALTreeParserTokenTypes.QUALIFIED_VAR, CALTreeParserTokenTypes.SELECT_DATA_CONSTRUCTOR_FIELD, CALTreeParserTokenTypes.VIRTUAL_TUPLE_CASE, CALTreeParserTokenTypes.VIRTUAL_RECORD_CASE, CALTreeParserTokenTypes.VIRTUAL_DATA_CONSTRUCTOR_CASE); return (ErrorInfo)getAttributeValue(AttributeKey.ERROR_INFO_FOR_ERROR_CALL); } /** * Associates an unused variable name with a node representing a wildcard pattern variable, storing it in the attributes map. * @param unusedVarName the unused variable name. */ void setUnusedVarNameForWildcardPatternVar(String unusedVarName) { verifyType(CALTreeParserTokenTypes.UNDERSCORE); putAttributeValue(AttributeKey.UNUSED_VAR_NAME_FOR_WILDCARD_PATTERN_VAR, unusedVarName); } /** * Returns the unused variable name associated with a node representing a wildcard pattern variable, fetching it from the attributes map. * @return the unused variable name. */ String getUnusedVarNameForWildcardPatternVar() { verifyType(CALTreeParserTokenTypes.UNDERSCORE); return (String)getAttributeValue(AttributeKey.UNUSED_VAR_NAME_FOR_WILDCARD_PATTERN_VAR); } /** * Verifies this node is an INTEGER_LITERAL node or a MINUS node representing a negated integer. */ private void verifyTypeAsMaybeMinusIntLiteral() { if (getType() == CALTreeParserTokenTypes.MINUS) { firstChild().verifyType(CALTreeParserTokenTypes.INTEGER_LITERAL); } else { verifyType(CALTreeParserTokenTypes.INTEGER_LITERAL); } } /** * Associates an Integer value with an INTEGER_LITERAL node or a MINUS node representing a negated integer. * @param integerValue the Integer value to be associated as the literal representation. */ void setIntegerValueForMaybeMinusIntLiteral(Integer integerValue) { verifyTypeAsMaybeMinusIntLiteral(); putAttributeValue(AttributeKey.LITERAL_VALUE_FOR_MAYBE_MINUS_INT_LITERAL, integerValue); } /** * Associates a Byte value with an INTEGER_LITERAL node or a MINUS node representing a negated integer. * @param byteValue the Byte value to be associated as the literal representation. */ void setByteValueForMaybeMinusIntLiteral(Byte byteValue) { verifyTypeAsMaybeMinusIntLiteral(); putAttributeValue(AttributeKey.LITERAL_VALUE_FOR_MAYBE_MINUS_INT_LITERAL, byteValue); } /** * Associates a Short value with an INTEGER_LITERAL node or a MINUS node representing a negated integer. * @param shortValue the Short value to be associated as the literal representation. */ void setShortValueForMaybeMinusIntLiteral(Short shortValue) { verifyTypeAsMaybeMinusIntLiteral(); putAttributeValue(AttributeKey.LITERAL_VALUE_FOR_MAYBE_MINUS_INT_LITERAL, shortValue); } /** * Associates a Long value with an INTEGER_LITERAL node or a MINUS node representing a negated integer. * @param longValue the Long value to be associated as the literal representation. */ void setLongValueForMaybeMinusIntLiteral(Long longValue) { verifyTypeAsMaybeMinusIntLiteral(); putAttributeValue(AttributeKey.LITERAL_VALUE_FOR_MAYBE_MINUS_INT_LITERAL, longValue); } /** * Associates a Long value with an INTEGER_LITERAL node or a MINUS node representing a negated integer. * @param bigIntegerValue the BigInteger value to be associated as the literal representation. */ void setBigIntegerValueForMaybeMinusIntLiteral(BigInteger bigIntegerValue) { verifyTypeAsMaybeMinusIntLiteral(); putAttributeValue(AttributeKey.LITERAL_VALUE_FOR_MAYBE_MINUS_INT_LITERAL, bigIntegerValue); } /** * Retrieves the (Byte/Short/Integer/Long/BigInteger) value associated with an INTEGER_LITERAL node or * a MINUS node representing a negated integer. * @return the associated literal value. */ Number getLiteralValueForMaybeMinusIntLiteral() { verifyTypeAsMaybeMinusIntLiteral(); return (Number)getAttributeValue(AttributeKey.LITERAL_VALUE_FOR_MAYBE_MINUS_INT_LITERAL); } /** * Associates a Float value with a FLOAT_LITERAL node, storing it in the attributes map. * @param floatValue the Float value to be associated as the literal representation. */ void setFloatValueForFloatLiteral(Float floatValue) { verifyType(CALTreeParserTokenTypes.FLOAT_LITERAL); putAttributeValue(AttributeKey.LITERAL_VALUE_FOR_FLOAT_LITERAL, floatValue); } /** * Associates a Double value with a FLOAT_LITERAL node, storing it in the attributes map. * @param doubleValue the Double value to be associated as the literal representation. */ void setDoubleValueForFloatLiteral(Double doubleValue) { verifyType(CALTreeParserTokenTypes.FLOAT_LITERAL); putAttributeValue(AttributeKey.LITERAL_VALUE_FOR_FLOAT_LITERAL, doubleValue); } /** * Retrieves the (Float/Double) value associated with a FLOAT_LITERAL node, fetching it from the attributes map. * @return the associated literal value. */ Number getLiteralValueForFloatLiteral() { verifyType(CALTreeParserTokenTypes.FLOAT_LITERAL); return (Number)getAttributeValue(AttributeKey.LITERAL_VALUE_FOR_FLOAT_LITERAL); } /** * Associates a Character value with a CHAR_LITERAL node, storing it in the attributes map. * @param characterValue the Character value to be associated as the literal representation. */ void setCharacterValueForCharLiteral(Character characterValue) { verifyType(CALTreeParserTokenTypes.CHAR_LITERAL); putAttributeValue(AttributeKey.CHARACTER_VALUE_FOR_CHAR_LITERAL, characterValue); } /** * Retrieves the Character value associated with a FLOAT_LITERAL node, fetching it from the attributes map. * @return the associated Character value. */ Character getCharacterValueForCharLiteral() { verifyType(CALTreeParserTokenTypes.CHAR_LITERAL); return (Character)getAttributeValue(AttributeKey.CHARACTER_VALUE_FOR_CHAR_LITERAL); } /** * Associates a boolean flag marking a node as synthetic with a VAR_ID or SELECT_RECORD_FIELD node, storing it in the attributes map. * @param isSynthetic boolean flag marking a node as synthetic. */ void setIsSyntheticVarOrRecordFieldSelection(boolean isSynthetic) { verifyType(CALTreeParserTokenTypes.VAR_ID, CALTreeParserTokenTypes.SELECT_RECORD_FIELD); putAttributeValue(AttributeKey.IS_SYNTHETIC_VAR_OR_RECORD_FIELD_SELECTION, Boolean.valueOf(isSynthetic)); } /** * Retrieves the boolean flag marking a node as synthetic with a VAR_ID or SELECT_RECORD_FIELD node, fetching it from the attributes map. * @return the boolean flag marking a node as synthetic. */ boolean getIsSyntheticVarOrRecordFieldSelection() { verifyType(CALTreeParserTokenTypes.VAR_ID, CALTreeParserTokenTypes.SELECT_RECORD_FIELD); Boolean flag = (Boolean)getAttributeValue(AttributeKey.IS_SYNTHETIC_VAR_OR_RECORD_FIELD_SELECTION); return (flag != null) && flag.booleanValue(); } /** * Associates a boolean flag marking a LET_DEFN node as being a desugared definition for a local pattern match declaration, storing it in the attributes map. * @param isDesugaredPatternMatch the boolean flag marking a LET_DEFN node as being a desugared definition for a local pattern match declaration. */ void setIsDesugaredPatternMatchForLetDefn(boolean isDesugaredPatternMatch) { verifyType(CALTreeParserTokenTypes.LET_DEFN); putAttributeValue(AttributeKey.IS_DESUGARED_PATTERN_MATCH_FOR_LET_DEFN, Boolean.valueOf(isDesugaredPatternMatch)); } /** * Retrieves the boolean flag marking a LET_DEFN node as being a desugared definition for a local pattern match declaration, fetching it from the attributes map. * @return the boolean flag marking a LET_DEFN node as being a desugared definition for a local pattern match declaration. */ boolean getIsDesugaredPatternMatchForLetDefn() { verifyType(CALTreeParserTokenTypes.LET_DEFN); Boolean flag = (Boolean)getAttributeValue(AttributeKey.IS_DESUGARED_PATTERN_MATCH_FOR_LET_DEFN); return (flag != null) && flag.booleanValue(); } /** * Associates a SortedSet of {@link FieldName}s corresponding to non-polymorphic record patterns appearing in a local pattern match declaration, storing it in the attributes map. * @param fieldNames the SortedSet of {@link FieldName}s. */ void setDeclaredFieldsInNonPolymorphicRecordPatternMatchForLetDefn(final SortedSet<FieldName> fieldNames) { verifyType(CALTreeParserTokenTypes.LET_DEFN); putAttributeValue(AttributeKey.DECLARED_FIELDS_IN_NON_POLYMORPHIC_RECORD_PATTERN_MATCH_FOR_LET_DEFN, fieldNames); } /** * Retrieves the SortedSet of {@link FieldName}s corresponding to non-polymorphic record patterns appearing in a local pattern match declaration, fetching it from the attributes map. * @return the SortedSet of {@link FieldName}s. */ SortedSet<FieldName> getDeclaredFieldsInNonPolymorphicRecordPatternMatchForLetDefn() { verifyType(CALTreeParserTokenTypes.LET_DEFN); return UnsafeCast.unsafeCast(getAttributeValue(AttributeKey.DECLARED_FIELDS_IN_NON_POLYMORPHIC_RECORD_PATTERN_MATCH_FOR_LET_DEFN)); } /** * Associates a SortedSet of {@link FieldName}s corresponding to polymorphic record patterns appearing in a local pattern match declaration, storing it in the attributes map. * @param fieldNames the SortedSet of {@link FieldName}s. */ void setDeclaredFieldsInPolymorphicRecordPatternMatchForLetDefn(final SortedSet<FieldName> fieldNames) { verifyType(CALTreeParserTokenTypes.LET_DEFN); putAttributeValue(AttributeKey.DECLARED_FIELDS_IN_POLYMORPHIC_RECORD_PATTERN_MATCH_FOR_LET_DEFN, fieldNames); } /** * Retrieves the SortedSet of {@link FieldName}s corresponding to polymorphic record patterns appearing in a local pattern match declaration, fetching it from the attributes map. * @return the SortedSet of {@link FieldName}s. */ SortedSet<FieldName> getDeclaredFieldsInPolymorphicRecordPatternMatchForLetDefn() { verifyType(CALTreeParserTokenTypes.LET_DEFN); return UnsafeCast.unsafeCast(getAttributeValue(AttributeKey.DECLARED_FIELDS_IN_POLYMORPHIC_RECORD_PATTERN_MATCH_FOR_LET_DEFN)); } /** * Associates the specified attribute value with the specified key, performing lazy construction of the map on the first call. * @param key the attribute key. * @param value the attribute value to be associated with the key. */ private void putAttributeValue(AttributeKey key, Object value) { if (attributes == null) { attributes = new HashMap<AttributeKey, Object>(); } attributes.put(key, value); } /** * Retrieves the value associated with the specified attribute key. If the attributes map has not been instantiated, then * null is returned. * @param key the attribute key. * @return the attribute value associated with the key, which could be null if the attributes map 1) is null itself, * 2) does not contain the mapping, or 3) has stored null as the value associated with they key. */ private Object getAttributeValue(AttributeKey key) { if (attributes == null) { return null; } return attributes.get(key); } /** * Copies an attributes map into a new map, performing deep copies of attribute values when necessary. * If the original map is null, then null is returned. * @param original the map to be copied. Can be null. * @return a copy of the original map, or null if the original map is null. */ private static Map<AttributeKey, Object> makeAttributesCopyFrom(Map<AttributeKey, Object> original) { if (original == null) { return null; } Map<AttributeKey, Object> result = new HashMap<AttributeKey, Object>(); for (final Map.Entry<AttributeKey, Object> entry : original.entrySet()) { AttributeKey key = entry.getKey(); Object value = entry.getValue(); // copy the value deeply, if required Object copyOfValue; if (value instanceof TypeExpr) { copyOfValue = ((TypeExpr)value).copyTypeExpr(); } else { copyOfValue = value; } // put the copy of the value in the result map with the original key result.put(key, copyOfValue); } return result; } /** * Initialize the node from a token. * Get the node's CAL source position (line and column) from the token as well. * Creation date: (12/6/00 8:29:56 AM) */ @Override public void initialize(Token tok) { super.initialize(tok); SourcePosition sourcePosition = new SourcePosition(tok.getLine(), tok.getColumn(), tok.getFilename()); if(tok.getText() != null) { sourceRange = new SourceRange(sourcePosition, tok.getText()); } else { sourceRange = new SourceRange(sourcePosition, sourcePosition); } } /** * For SourceRange handling of "paired" lexemes (eg, '(' and ')'). * We usually retain only the first such lexeme; however, the SourceRange for * the retained lexeme should extend until the end of the closing lexeme in * the pair. * * This method is called by the parser to allow us to update the source range * for the retained lexeme to include the extents of omitted lexemes (such as * the close delimiter in the example above). * @param omittedDelimiter Node representing an omitted delimiter that should * be included in the SourceRange for this node. */ void addOmittedDelimiter(AST omittedDelimiter) { ParseTreeNode omittedDelimiterNode = (ParseTreeNode)omittedDelimiter; SourceRange omittedRange = omittedDelimiterNode.getSourceRange(); if(sourceRange == null) { sourceRange = omittedRange; } else { SourcePosition startPos = sourceRange.getStartSourcePosition(); SourcePosition endPos = sourceRange.getEndSourcePosition(); if(SourcePosition.compareByPosition.compare(omittedRange.getStartSourcePosition(), startPos) < 0) { startPos = omittedRange.getStartSourcePosition(); } if(SourcePosition.compareByPosition.compare(omittedRange.getEndSourcePosition(), endPos) > 0) { endPos = omittedRange.getEndSourcePosition(); } sourceRange = new SourceRange(startPos, endPos); } } /** * Throws an exception for an unexpected parse tree node. Note that the various overloads * of verifyType are preferred to using this method since they will give a better * error message. This is mainly used when there are too many possible excepted nodes. */ final void unexpectedParseTreeNode() { throw makeExceptionForUnexpectedParseTreeNode(); } /** * @return an exception that can be thrown for an unexpected parse tree node. */ final IllegalStateException makeExceptionForUnexpectedParseTreeNode() { return new IllegalStateException("Unexpected parse tree node " + toDebugString() + "."); } /** * Verifies that this node has the specified nodeType. This error should never * occur in production code and is mostly intended as a form of documentation and to * catch errors during the development of CAL. * @param nodeType selected from the constants in CALTreeParserTokenTypes * @throws IllegalStateException if this ParseTreeNode does not have the node type given by the argument. */ final void verifyType(int nodeType) { if (getType() != nodeType) { throw new IllegalStateException("Unexpected parse tree node " + toDebugString() + ". Expecting node type " + getTypeInfo(nodeType) + "."); } } /** * Verifies that this node has one of the specified nodeType. This error should never * occur in production code and is mostly intended as a form of documentation and to * catch errors during the development of CAL. * @param nodeType1 selected from the constants in CALTreeParserTokenTypes * @param nodeType2 selected from the constants in CALTreeParserTokenTypes * @throws IllegalStateException if this ParseTreeNode does not have the node type given by the argument. */ final void verifyType(int nodeType1, int nodeType2) { int nodeType = getType(); if (nodeType != nodeType1 && nodeType != nodeType2) { throw new IllegalStateException("Unexpected parse tree node " + toDebugString() + ". Expecting node type " + getTypeInfo(nodeType1) + " or " + getTypeInfo(nodeType2) + "."); } } /** * Verifies that this node has one of the specified nodeType. This error should never * occur in production code and is mostly intended as a form of documentation and to * catch errors during the development of CAL. * @param nodeType1 selected from the constants in CALTreeParserTokenTypes * @param nodeType2 selected from the constants in CALTreeParserTokenTypes * @param nodeType3 selected from the constants in CALTreeParserTokenTypes * @throws IllegalStateException if this ParseTreeNode does not have the node type given by the argument. */ final void verifyType(int nodeType1, int nodeType2, int nodeType3) { int nodeType = getType(); if (nodeType != nodeType1 && nodeType != nodeType2 && nodeType != nodeType3) { throw new IllegalStateException("Unexpected parse tree node " + toDebugString() + ". Expecting node type " + getTypeInfo(nodeType1) + ", " + getTypeInfo(nodeType2) + " or " + getTypeInfo(nodeType3) + "."); } } /** * Verifies that this node has one of the specified nodeType. This error should never * occur in production code and is mostly intended as a form of documentation and to * catch errors during the development of CAL. * @param nodeType1 selected from the constants in CALTreeParserTokenTypes * @param nodeType2 selected from the constants in CALTreeParserTokenTypes * @param nodeType3 selected from the constants in CALTreeParserTokenTypes * @param nodeType4 selected from the constants in CALTreeParserTokenTypes * @throws IllegalStateException if this ParseTreeNode does not have the node type given by the argument. */ final void verifyType(int nodeType1, int nodeType2, int nodeType3, int nodeType4) { int nodeType = getType(); if (nodeType != nodeType1 && nodeType != nodeType2 && nodeType != nodeType3 && nodeType != nodeType4) { throw new IllegalStateException("Unexpected parse tree node " + toDebugString() + ". Expecting node type " + getTypeInfo(nodeType1) + ", " + getTypeInfo(nodeType2) + " or " + getTypeInfo(nodeType3) + " or " + getTypeInfo(nodeType4) + "."); } } /** * Verifies that this node has one of the specified nodeType. This error should never * occur in production code and is mostly intended as a form of documentation and to * catch errors during the development of CAL. * @param nodeType1 selected from the constants in CALTreeParserTokenTypes * @param nodeType2 selected from the constants in CALTreeParserTokenTypes * @param nodeType3 selected from the constants in CALTreeParserTokenTypes * @param nodeType4 selected from the constants in CALTreeParserTokenTypes * @param nodeType5 selected from the constants in CALTreeParserTokenTypes * @throws IllegalStateException if this ParseTreeNode does not have the node type given by the argument. */ final void verifyType(int nodeType1, int nodeType2, int nodeType3, int nodeType4, int nodeType5) { int nodeType = getType(); if (nodeType != nodeType1 && nodeType != nodeType2 && nodeType != nodeType3 && nodeType != nodeType4 && nodeType != nodeType5) { throw new IllegalStateException("Unexpected parse tree node " + toDebugString() + ". Expecting node type " + getTypeInfo(nodeType1) + ", " + getTypeInfo(nodeType2) + " or " + getTypeInfo(nodeType3) + " or " + getTypeInfo(nodeType4) + " or " + getTypeInfo(nodeType5) + "."); } } /** * Verifies that this node has one of the specified nodeType. This error should never * occur in production code and is mostly intended as a form of documentation and to * catch errors during the development of CAL. * @param nodeType1 selected from the constants in CALTreeParserTokenTypes * @param nodeType2 selected from the constants in CALTreeParserTokenTypes * @param nodeType3 selected from the constants in CALTreeParserTokenTypes * @param nodeType4 selected from the constants in CALTreeParserTokenTypes * @param nodeType5 selected from the constants in CALTreeParserTokenTypes * @param nodeType6 selected from the constants in CALTreeParserTokenTypes * @throws IllegalStateException if this ParseTreeNode does not have the node type given by the argument. */ final void verifyType(int nodeType1, int nodeType2, int nodeType3, int nodeType4, int nodeType5, int nodeType6) { int nodeType = getType(); if (nodeType != nodeType1 && nodeType != nodeType2 && nodeType != nodeType3 && nodeType != nodeType4 && nodeType != nodeType5 && nodeType != nodeType6) { throw new IllegalStateException("Unexpected parse tree node " + toDebugString() + ". Expecting node type " + getTypeInfo(nodeType1) + ", " + getTypeInfo(nodeType2) + " or " + getTypeInfo(nodeType3) + " or " + getTypeInfo(nodeType4) + " or " + getTypeInfo(nodeType5) + " or " + getTypeInfo(nodeType6)+ "."); } } /** * Method getTypeInfo. * @param nodeType selected from the constants in CALTreeParserTokenTypes * @return a textual description of the nodeType */ private static String getTypeInfo(int nodeType) { String result; try { result = CALTreeParser._tokenNames[nodeType]; } catch (IndexOutOfBoundsException e) { result = "" + nodeType; } return result; } /** * Returns a longer textual display of debug info for this node. We don't override toString since * this has the undesirable side effect of changing the behavior of toStringTree. * Creation date: (6/18/01 5:22:38 PM) * @return String */ String toDebugString() { StringBuilder result = new StringBuilder(); result.append('['); result.append("type = ").append(getTypeInfo(getType())); result.append(", text = ").append(getText()); result.append(", sourceRange = ").append(sourceRange); result.append(']'); return result.toString(); } /** * A useful alternative to toDebugString when debugging the CAL compiler. * For example, it can be useful to put a breakpoint in ExpressionGenerator.generateSCDefn, * and then examine the parse tree for a particular sc to see that overload resolution and * lambda lifting have proceeded as planned. * @return a deep (i.e. includes children) XML representation of the ParseTreeNode suitable * for convenient viewing in an XML viewer such as Internet Explorer. */ String toXMLString() { Writer writer = new StringWriter(); try { xmlDump(writer); } catch (IOException e) { return null; } return writer.toString(); } /** * See the comment for toXMLString. * @param fileName * @throws IOException */ void xmlDumpToFile(String fileName) throws IOException{ File file = new File(fileName); Writer writer = new FileWriter(file); xmlDump(writer); writer.close(); } private void xmlDump(Writer out) throws IOException{ // print out this node and all children if (firstChild() == null) { // print node xmlDumpNode(out); } else { xmlDumpRootOpen(out); // print children for (final ParseTreeNode child : this) { child.xmlDump(out); } // print end tag xmlDumpRootClose(out); } } private void xmlDumpNode(Writer out) throws IOException { StringBuilder buf = new StringBuilder(100); buf.append("<node "); buf.append("t=\"" + encode(getText()) + "\"/>"); out.write(buf.toString()); } private void xmlDumpRootOpen(Writer out) throws IOException { StringBuilder buf = new StringBuilder(100); buf.append("<node "); buf.append("t=\"" + encode(getText()) + "\">\n"); out.write(buf.toString()); } private void xmlDumpRootClose(Writer out) throws IOException { out.write("</node>\n"); } /** * @return an iterator over the children of this ParseTreeNode. * * {@inheritDoc} */ public Iterator<ParseTreeNode> iterator() { return ParseTreeNodeIterator.make(this); } /** * @return can be used to iterate over the next siblings of this node * (not including this node) in a for-each loop. */ public Iterable<ParseTreeNode> nextSiblings() { return new Iterable<ParseTreeNode> () { public Iterator<ParseTreeNode> iterator() { return ParseTreeNodeIterator.nextSiblings(ParseTreeNode.this); } }; } /** * @return constructs a QualifiedName from a ParseTreeNode of type QUALIFIED_CONS or QUALIFIED_VAR. */ QualifiedName toQualifiedName() { if (getType() != CALTreeParserTokenTypes.QUALIFIED_CONS && getType() != CALTreeParserTokenTypes.QUALIFIED_VAR) { throw new IllegalArgumentException("QualifiedName constructor: unexpected parse tree node " + toDebugString()); } ParseTreeNode moduleNameNode = firstChild(); String moduleName = ModuleNameUtilities.getMaybeModuleNameStringFromParseTree(moduleNameNode); ParseTreeNode nameNode = moduleNameNode.nextSibling(); String unqualifiedName = nameNode.getText(); return QualifiedName.make(ModuleName.make(moduleName), unqualifiedName); } }