Пример #1
0
        // =======================================================================================
        // CLASS INITIALISATION
        // =======================================================================================
        /// <summary>
        /// A statement should be solely an expression with no return value or whose return value is ignored - eg. a function call. Tokens that
        /// represent a statement where the return value is used to set another variable's value should be described by a ValueSettingStatement.
        /// It is recommended to pass any tokens that are thought to be one Statement through the StatementHandler, which will break down the
        /// content into multiple Statements (if there are any AbstractEndOfStatementToken tokens).
        /// </summary>
        public Statement(IEnumerable <IToken> tokens, CallPrefixOptions callPrefix)
        {
            if (tokens == null)
            {
                throw new ArgumentNullException("tokens");
            }
            if (!Enum.IsDefined(typeof(CallPrefixOptions), callPrefix))
            {
                throw new ArgumentOutOfRangeException("callPrefix");
            }

            Tokens = tokens.ToList().AsReadOnly();
            if (!Tokens.Any())
            {
                throw new ArgumentException("Statements must contain at least one token");
            }
            if (!Tokens.Any())
            {
                throw new ArgumentException("Empty tokens specified - invalid");
            }
            if (Tokens.Any(t => t == null))
            {
                throw new ArgumentException("Null token passed into Statement constructor");
            }
            var firstTokenAsAtom = tokens.First() as AtomToken;

            if ((firstTokenAsAtom != null) && firstTokenAsAtom.Content.Equals("Call", StringComparison.InvariantCultureIgnoreCase))
            {
                throw new ArgumentException("The first token may not be the Call keyword, that must be specified through the CallPrefixOption value where present");
            }

            CallPrefix = callPrefix;
        }
Пример #2
0
        // =======================================================================================
        // STANDARDISE BRACKETS, REMOVE VBScript WOBBLINESS
        //   "Test 1" bad
        //   "Test(1) good
        //   "a.Test 1" bad
        //   "a.Test(1)" good
        //   "a(0).Test 1" bad
        //   "a(0).Test(1)" good
        // Note: Allow "Test" or "a.Test", if there are no arguments then it's less important
        // 2014-03-24 DWR: This has been tightened up since "Test a" and "Test(a)" are not the
        // same to VBScript, the latter actually associates the brackets with the "a" and not
        // the "Test" call, brackets around an argument to function mean that the argument
        // should be passed ByVal, not ByRef - if the argument would otherwise be ByRef.
        // This is why "Test(1, 2)" is not valid (the error "Cannot use parentheses when calling
        // a Sub" will be raised) since the parentheses are associated with the function call,
        // which is not acceptable when not considering the return value - eg. "a = Test(1, 2)"
        // IS acceptable since the return value IS being considered. As such, the following
        // transformations should be applied:
        //   "Test a1" => "Test(a1)" (argument not forced to be ByVal)
        //   "Test(a1)" => "Test((a1))" (argument forced to be ByVal)
        //   "r = Test a1" is invalid ("Expected end of statement")
        //   "r = Test(a1)" requires no transformation (ByVal not enforced)
        //   "r = Test((a1))" requires no transformation (ByVal IS enforced)
        //   "Test(a1, a2)" is invalid ("Cannot use parentheses when calling a Sub")
        //   "r = Test(a1, a2)" requires no transformation (ByVal not enforced)
        //   "r = Test((a1), a2)" requires no transformation (ByVal enforced for a1 but not a2)
        //   "CALL Test(a1, a2)" requires no transformation (ByVal not enforced)
        //   "CALL Test((a1), a2)" requires no transformation (ByVal enforced for a1 but not a2)
        // See http://blogs.msdn.com/b/ericlippert/archive/2003/09/15/52996.aspx
        // (In all of the above examples, the value-setting statements such as "r = Test(a1)"
        // would not be represented by this class, the ValueSettingStatment class would be used,
        // but the examples are included to illustrate variations on the cases that must be
        // dealt with),
        // =======================================================================================
        private static IEnumerable <IToken> GetBracketStandardisedTokens(IEnumerable <IToken> tokens, CallPrefixOptions callPrefix, NameToken withReferenceIfAny)
        {
            if (tokens == null)
            {
                throw new ArgumentNullException("tokens");
            }
            if (!Enum.IsDefined(typeof(CallPrefixOptions), callPrefix))
            {
                throw new ArgumentOutOfRangeException("callPrefix");
            }

            var tokenArray = tokens.ToArray();

            if (tokenArray.Length == 0)
            {
                throw new ArgumentException("Empty tokens set specified - invalid for a Statement");
            }

            // TODO: Add a method that will detect all forms of invalid content first and require that this method be called before the Statement is
            // processed further (dealing with all run time or compile time errors). Such a method would enable the work to be performed under the
            // assumption that the content was valid VBScript, which would make things easier. (Should this method be in a translator and this data
            // be returned from a method GetBracketStandardisedTokensIfContentValid?)

            // No need to try to re-arrange things if this is a new-instance expression, no brackets are required
            if ((tokenArray[0] is KeyWordToken) && tokenArray[0].Content.Equals("new", StringComparison.InvariantCultureIgnoreCase))
            {
                return(tokenArray);
            }

            // If the first token is a member access and we're inside a with block then insert the with block's target reference token into the start
            // of the token stream. This isn't strictly necessary to standardise the brackets but it makes it easier to do so.
            if ((tokenArray[0] is MemberAccessorOrDecimalPointToken) && (withReferenceIfAny != null))
            {
                tokenArray = new IToken[] { withReferenceIfAny }
            }