Click here to Skip to main content
11,494,174 members (60,379 online)
Click here to Skip to main content
Add your own
alternative version

Resolving Symbolic References in a CodeDOM (Part 7)

, 2 Dec 2012 CDDL 5.8K 333 13
Resolving symbolic references in a CodeDOM.
Nova.0.6.exe.zip
Mono.Cecil.dll
Nova.CLI.exe
Nova.CodeDOM.dll
Nova.Examples.exe
Nova.Studio.exe
Nova.Test.exe
Nova.UI.dll
Nova.0.6.zip
Nova.CLI
Properties
Nova.CodeDOM
CodeDOM
Annotations
Base
Comments
Base
DocComments
CodeRef
Base
List
Name
Base
Other
Simple
CompilerDirectives
Base
Conditionals
Base
Messages
Base
Pragmas
Base
Symbols
Base
Base
Interfaces
Expressions
AnonymousMethods
Base
Operators
Base
Binary
Arithmetic
Base
Assignment
Base
Bitwise
Base
Conditional
Relational
Base
Shift
Base
Other
Base
Unary
Base
Other
References
Base
GotoTargets
Base
Methods
Namespaces
Other
Properties
Types
Base
Variables
Base
Projects
Assemblies
Namespaces
References
Base
Statements
Base
Conditionals
Base
Exceptions
Generics
Constraints
Base
Iterators
Base
Jumps
Loops
Methods
OperatorDecls
Miscellaneous
Namespaces
Properties
Base
Events
Types
Base
Variables
Base
Parsing
Base
Properties
Rendering
Resolving
Utilities
Mono.Cecil
Reflection
Nova.Examples
Properties
Nova.Studio
Images
About.png
Configuration.png
EditCopy.png
EditCut.png
EditDelete.png
EditPaste.png
EditRedo.png
EditUndo.png
Error.png
Exit.png
FileNew.png
FileOpen.png
FileSave.png
FileSaveAll.png
FileSaveAs.png
Find.png
Help.png
Info.png
Logo.png
Options.png
Print.png
PrintPreview.png
Properties.png
Todo.png
Warning.png
Objects.ico
Properties
Settings.settings
Nova.Test
Properties
Nova.UI
CodeDOM
Annotations
Base
Comments
Base
DocComments
CodeRef
Base
List
Name
Base
Other
Simple
CompilerDirectives
Base
Conditionals
Base
Messages
Base
Pragmas
Base
Symbols
Base
Base
Expressions
AnonymousMethods
Base
Operators
Base
Binary
Arithmetic
Base
Assignment
Base
Bitwise
Base
Conditional
Relational
Base
Shift
Base
Other
Base
Unary
Base
Other
References
Base
GotoTargets
Base
Methods
Namespaces
Other
Properties
Types
Base
Variables
Base
Projects
Namespaces
References
Base
Statements
Base
Conditionals
Base
Exceptions
Generics
Constraints
Base
Iterators
Base
Jumps
Loops
Methods
OperatorDecls
Miscellaneous
Namespaces
Properties
Base
Events
Types
Base
Variables
Base
Properties
Resolving
Utilties
// 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)

Share

About the Author

KenBeckett
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).

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.150520.1 | Last Updated 2 Dec 2012
Article Copyright 2012 by KenBeckett
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid