Click here to Skip to main content
15,895,746 members
Articles / Programming Languages / C#

Resolving Symbolic References in a CodeDOM (Part 7)

Rate me:
Please Sign up or sign in to vote.
4.75/5 (6 votes)
2 Dec 2012CDDL12 min read 19.4K   510   14  
Resolving symbolic references in a CodeDOM.
// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php

using Nova.Parsing;
using Nova.Rendering;
using Nova.Resolving;

namespace Nova.CodeDOM
{
    /// <summary>
    /// Represents the declaration of a parameter to a method.
    /// </summary>
    /// <remarks>
    /// A ParameterDecl can optionally have a modifier of:
    ///     ref, out, params (last parameter only)
    /// Parameters don't support compound (multi) declarations like other VariableDecls.
    /// </remarks>
    public class ParameterDecl : VariableDecl
    {
        #region /* FIELDS */

        protected ParameterModifier _modifier;

        #endregion

        #region /* CONSTRUCTORS */

        /// <summary>
        /// Create a <see cref="ParameterDecl"/>.
        /// </summary>
        /// <param name="name">The name of the parameter.</param>
        /// <param name="type">The type of the parameter</param>
        /// <param name="defaultValue">The default value expression for the parameter.</param>
        public ParameterDecl(string name, Expression type, Expression defaultValue)
            : base(name, type, defaultValue)
        { }

        /// <summary>
        /// Create a <see cref="ParameterDecl"/>.
        /// </summary>
        /// <param name="name">The name of the parameter.</param>
        /// <param name="type">The type of the parameter</param>
        public ParameterDecl(string name, Expression type)
            : base(name, type, null)
        { }

        /// <summary>
        /// Create a <see cref="ParameterDecl"/>.
        /// </summary>
        /// <param name="name">The name of the parameter.</param>
        /// <param name="type">The type of the parameter</param>
        /// <param name="modifier">The ParameterModifier for the parameter.</param>
        /// <param name="defaultValue">The default value expression for the parameter.</param>
        public ParameterDecl(string name, Expression type, ParameterModifier modifier, Expression defaultValue)
            : base(name, type, defaultValue)
        {
            _modifier = modifier;
        }

        /// <summary>
        /// Create a <see cref="ParameterDecl"/>.
        /// </summary>
        /// <param name="name">The name of the parameter.</param>
        /// <param name="type">The type of the parameter</param>
        /// <param name="modifier">The ParameterModifier for the parameter.</param>
        public ParameterDecl(string name, Expression type, ParameterModifier modifier)
            : base(name, type, null)
        {
            _modifier = modifier;
        }

        #endregion

        #region /* PROPERTIES */

        /// <summary>
        /// The descriptive category of the code object.
        /// </summary>
        public override string Category
        {
            get { return "parameter"; }
        }

        /// <summary>
        /// The <see cref="ParameterModifier"/> for the parameter, if any.
        /// </summary>
        public ParameterModifier Modifier
        {
            get { return _modifier; }
        }

        /// <summary>
        /// Determines if the parameter is a 'params' parameter.
        /// </summary>
        public bool IsParams
        {
            get { return (_modifier == ParameterModifier.Params); }
            set { _modifier = (value ? ParameterModifier.Params : (IsParams ? ParameterModifier.None : _modifier)); }
        }

        /// <summary>
        /// Determines if the parameter is being passed by reference.
        /// </summary>
        public bool IsRef
        {
            get { return (_modifier == ParameterModifier.Ref); }
            set { _modifier = (value ? ParameterModifier.Ref : (IsRef ? ParameterModifier.None : _modifier)); }
        }

        /// <summary>
        /// Determines if the parameter is being passed out only.
        /// </summary>
        public bool IsOut
        {
            get { return (_modifier == ParameterModifier.Out); }
            set { _modifier = (value ? ParameterModifier.Out : (IsOut ? ParameterModifier.None : _modifier)); }
        }

        /// <summary>
        /// Always <c>false</c> for a parameter.
        /// </summary>
        public override bool IsConst
        {
            get { return false; }
            set { }
        }

        /// <summary>
        /// Always <c>false</c> for a parameter.
        /// </summary>
        public override bool IsStatic
        {
            get { return false; }
            set { }
        }

        #endregion

        #region /* METHODS */

        /// <summary>
        /// Attach an <see cref="Annotation"/> (<see cref="Comment"/>, <see cref="DocComment"/>, <see cref="Attribute"/>, <see cref="CompilerDirective"/>, or <see cref="Message"/>) to the <see cref="CodeObject"/>.
        /// </summary>
        /// <param name="annotation">The <see cref="Annotation"/>.</param>
        /// <param name="atFront">Inserts at the front if true, otherwise adds at the end.</param>
        public override void AttachAnnotation(Annotation annotation, bool atFront)
        {
            // Force attached annotations to same-line for parameters
            annotation.IsFirstOnLine = false;
            base.AttachAnnotation(annotation, atFront);
        }

        /// <summary>
        /// Create a reference to the <see cref="ParameterDecl"/>.
        /// </summary>
        /// <param name="isFirstOnLine">True if the reference should be displayed on a new line.</param>
        /// <returns>A <see cref="ParameterRef"/>.</returns>
        public override SymbolicRef CreateRef(bool isFirstOnLine)
        {
            return new ParameterRef(this, isFirstOnLine);
        }

        #endregion

        #region /* PARSING */

        /// <summary>
        /// The token used to parse the start of a parameter list.
        /// </summary>
        public const string ParseTokenStart = Expression.ParseTokenStartGroup;

        /// <summary>
        /// The token used to parse the end of a parameter list.
        /// </summary>
        public const string ParseTokenEnd = Expression.ParseTokenEndGroup;

        /// <summary>
        /// The token used to parse between parameters.
        /// </summary>
        public const string ParseTokenSeparator = Expression.ParseTokenSeparator;

        /// <summary>
        /// The token used to parse 'ref' parameters.
        /// </summary>
        public const string ParseTokenRef = "ref";

        /// <summary>
        /// The token used to parse 'out' parameters.
        /// </summary>
        public const string ParseTokenOut = "out";

        /// <summary>
        /// The token used to parse 'params' parameters.
        /// </summary>
        public const string ParseTokenParams = "params";

        /// <summary>
        /// The token used to parse 'this' parameters.
        /// </summary>
        public const string ParseTokenThis = "this";

        protected ParameterDecl(Parser parser, CodeObject parent)
            : base(parser, parent)
        { }

        protected ParameterDecl(Parser parser, CodeObject parent, string parseTokenEnd)
            : base(parser, parent)
        {
            // Get any comments after the '('
            if (parser.LastToken.Text == ParseTokenStart)
                MoveAllComments(parser.LastToken);

            // If we're starting with an attribute, ignore any newline parsed in the base constructor
            if (parser.TokenText == Attribute.ParseTokenStart)
                IsFirstOnLine = false;

            Attribute.ParseAttributes(parser, this);  // Parse any attributes

            // Parse the optional modifier
            _modifier = ParseParameterModifier(parser.TokenText);
            if (_modifier != ParameterModifier.None)
            {
                parent.MoveFormatting(parser.Token);
                parser.NextToken();  // Move past the modifier
            }

            ParseType(parser);  // Parse the type

            ParseName(parser, parseTokenEnd);
            ParseInitialization(parser, parent);  // Parse the initialization (if any)

            MoveEOLComment(parser.LastToken);  // Associate any skipped EOL comment
        }

        /// <summary>
        /// Parse a list of parameters.
        /// </summary>
        public static ChildList<ParameterDecl> ParseList(Parser parser, CodeObject parent, string parseTokenStart,
            string parseTokenEnd, bool forceEmpty, out bool isEndFirstOnLine)
        {
            ChildList<ParameterDecl> parameterDecls = null;
            isEndFirstOnLine = false;
            if (parser.TokenText == parseTokenStart)
            {
                Token lastToken = parser.Token;
                parser.NextToken();  // Move past '(' or '['

                // Force an empty collection (vs null) if the flag is set
                if (forceEmpty)
                    parameterDecls = new ChildList<ParameterDecl>(parent);

                // Create a string of possible terminators (assuming 1 char terminators for now)
                string terminators = parseTokenEnd + ParseTokenTerminator + Block.ParseTokenStart + Block.ParseTokenEnd + Index.ParseTokenEnd;

                while (parser.TokenText != null && (parser.TokenText.Length != 1 || terminators.IndexOf(parser.TokenText[0]) < 0))
                {
                    ParameterDecl parameterDecl = new ParameterDecl(parser, parent, parseTokenEnd);

                    // Move any preceeding comments to the current ParameterDecl
                    parameterDecl.MoveComments(lastToken);

                    if (parameterDecls == null)
                        parameterDecls = new ChildList<ParameterDecl>(parent);
                    parameterDecls.Add(parameterDecl);

                    lastToken = parser.Token;
                    if (parser.TokenText == ParseTokenSeparator)
                    {
                        parser.NextToken();  // Move past ','

                        // Associate any EOL comment on the ',' to the last ParameterDecl
                        parameterDecl.MoveEOLComment(lastToken, false, false);

                        // Move any remaining regular comments as Post comments, if on a line by themselves
                        if (parser.Token.IsFirstOnLine)
                            parameterDecl.MoveCommentsAsPost(lastToken);
                    }
                }

                if (parent.ParseExpectedToken(parser, parseTokenEnd))  // Move past ')' or ']'
                {
                    isEndFirstOnLine = parser.LastToken.IsFirstOnLine;
                    if (parameterDecls == null || parameterDecls.Count == 0)
                        parent.MoveAllComments(lastToken, false, false, AnnotationFlags.IsInfix1);
                    parent.MoveEOLComment(parser.LastToken);  // Associate any skipped EOL comment with the parent
                }
            }
            return parameterDecls;
        }

        protected static ParameterModifier ParseParameterModifier(string modifierName)
        {
            ParameterModifier modifier;
            switch (modifierName)
            {
                case ParseTokenRef:    modifier = ParameterModifier.Ref; break;
                case ParseTokenOut:    modifier = ParameterModifier.Out; break;
                case ParseTokenParams: modifier = ParameterModifier.Params; break;
                case ParseTokenThis:   modifier = ParameterModifier.This; break;
                default:               modifier = ParameterModifier.None; break;
            }
            return modifier;
        }

        #endregion

        #region /* RESOLVING */

        /// <summary>
        /// Resolve all child symbolic references, using the specified <see cref="ResolveCategory"/> and <see cref="ResolveFlags"/>.
        /// </summary>
        public override CodeObject Resolve(ResolveCategory resolveCategory, ResolveFlags flags)
        {
            if ((flags & (ResolveFlags.Phase1 | ResolveFlags.Phase2)) == 0)
                ResolveAttributes(flags);
            return base.Resolve(ResolveCategory.CodeObject, flags);
        }

        /// <summary>
        /// Resolve child code objects that match the specified name, moving up the tree until a complete match is found.
        /// </summary>
        public override void ResolveRefUp(string name, Resolver resolver)
        {
            // Use a special call to avoid wasting time searching method bodies and parameters for the
            // type of the parameter (assumes our parent is a MethodDeclBase or an AnonymousMethod,
            // with the skip method just moving up to the next level without doing any resolving - a
            // GenericMethodDecl overrides the skip method to check type parameters).
            if (_parent != null)
                _parent.ResolveRefUpSkipMethodBody(name, resolver);
        }

        /// <summary>
        /// Get the constant value of the variable (if any).
        /// </summary>
        /// <returns>An object of the type of the variable with a value of the constant used
        /// to initialize it (if any), or null if the variable isn't a constant.</returns>
        public override object GetValue()
        {
            // Always null for parameters, since they have no true initializer (just a default value), and
            // we don't yet perform runtime value tracing.
            return null;
        }

        #endregion

        #region /* FORMATTING */

        /// <summary>
        /// True if the code object defaults to starting on a new line.
        /// </summary>
        public override bool IsFirstOnLineDefault
        {
            get { return false; }
        }

        /// <summary>
        /// True if the <see cref="Statement"/> has a terminator character by default.
        /// </summary>
        public override bool HasTerminatorDefault
        {
            get { return false; }
        }

        /// <summary>
        /// The number of newlines preceeding the object (0 to N).
        /// </summary>
        public override int NewLines
        {
            get { return base.NewLines; }
            set
            {
                // If we're changing to zero, also change all prefix attributes to zero
                bool isFirstOnLine = (value != 0);
                if (_annotations != null && !isFirstOnLine && IsFirstOnLine)
                {
                    foreach (Annotation annotation in _annotations)
                    {
                        if (annotation is Attribute)
                            annotation.IsFirstOnLine = false;
                    }
                }

                base.NewLines = value;
            }
        }

        #endregion

        #region /* RENDERING */

        /// <summary>
        /// Format a <see cref="ParameterModifier"/> as a string.
        /// </summary>
        public static string ParameterModifierToString(ParameterModifier modifier)
        {
            switch (modifier)
            {
                case ParameterModifier.Ref:    return ParseTokenRef;
                case ParameterModifier.Out:    return ParseTokenOut;
                case ParameterModifier.Params: return ParseTokenParams;
                case ParameterModifier.This:   return ParseTokenThis;
            }
            return "";
        }

        protected override void AsTextStatement(CodeWriter writer, RenderFlags flags)
        {
            if (_modifier != ParameterModifier.None)
                writer.Write(ParameterModifierToString(_modifier) + " ");
            AsTextType(writer, flags);
            UpdateLineCol(writer, flags);
            writer.WriteIdentifier(_name, flags);
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)


Written By
Software Developer (Senior)
United States United States
I've been writing software since the late 70's, currently focusing mainly on C#.NET. I also like to travel around the world, and I own a Chocolate Factory (sadly, none of my employees are oompa loompas).

Comments and Discussions