public void IfThereAreZeroArgumentsThenSpecifyingArgumentProviderIsNotRequired()
        {
            var expressionToSet = new Expression(new IToken[]
            {
                new NameToken("a", 0),
                new MemberAccessorOrDecimalPointToken(".", 0),
                new NameToken("b", 0)
            });
            var expressionToSetTo = new Expression(new[]
            {
                new NumericValueToken("1", 0)
            });
            var expected = new TranslatedStatementContentDetails(
                "_.SET((Int16)1, this, _env.a, \"b\")",
                new NonNullImmutableList <NameToken>(new[] { new NameToken("a", 0) })
                );
            var scopeAccessInformation = GetEmptyScopeAccessInformation();
            var actual = GetDefaultValueSettingStatementTranslator().Translate(
                new ValueSettingStatement(
                    expressionToSet,
                    expressionToSetTo,
                    ValueSettingStatement.ValueSetTypeOptions.Let
                    ),
                scopeAccessInformation
                );

            Assert.Equal(expected, actual, new TranslatedStatementContentDetailsComparer());
        }
        public void ValueSettingTargetOfTypePropertyWithZeroArgumentBracketsResultsInTypeMismatch()
        {
            var expressionToSet = new Expression(new IToken[]
            {
                new NameToken("Name", 0),
                new OpenBrace(0),
                new CloseBrace(0)
            });
            var expressionToSetTo = new Expression(new[]
            {
                new NumericValueToken("1", 0)
            });
            var expected = new TranslatedStatementContentDetails(
                "_.SET((Int16)1, this, this, \"Name\")",
                new NonNullImmutableList <NameToken>(new[] { new NameToken("Name", 0) })
                );
            var scopeAccessInformation = AddPropertyToScope(GetEmptyScopeAccessInformation(), "Name", 0);
            var actual = GetDefaultValueSettingStatementTranslator().Translate(
                new ValueSettingStatement(
                    expressionToSet,
                    expressionToSetTo,
                    ValueSettingStatement.ValueSetTypeOptions.Let
                    ),
                scopeAccessInformation
                );

            Assert.Equal(expected, actual, new TranslatedStatementContentDetailsComparer());
        }
        public void UndeclaredSimpleValueTypeUpdateToBoolean()
        {
            var expressionToSet = new Expression(new IToken[]
            {
                new NameToken("a", 0)
            });
            var expressionToSetTo = new Expression(new[]
            {
                new BuiltInValueToken("true", 0)
            });
            var expected = new TranslatedStatementContentDetails(
                "_env.a = true",
                new NonNullImmutableList <NameToken>(new[] { new NameToken("a", 0) })
                );
            var scopeAccessInformation = GetEmptyScopeAccessInformation();
            var actual = GetDefaultValueSettingStatementTranslator().Translate(
                new ValueSettingStatement(
                    expressionToSet,
                    expressionToSetTo,
                    ValueSettingStatement.ValueSetTypeOptions.Let
                    ),
                scopeAccessInformation
                );

            Assert.Equal(expected, actual, new TranslatedStatementContentDetailsComparer());
        }
        public void BuiltInFunctionsAreMappedToTheSupportClassButMayNotBeCalledDirectlyIfArgumentCountsMatch()
        {
            // This is a complement to BuiltInFunctionsAreMappedToTheSupportClassAndMayBeCalledDirectlyIfArgumentCountsMatch, where an incorrect number of
            // arguments is being passed to a support function. As such, it may not be called directly and must pass through the "CALL" method, so that the
            // mistake becomes a runtime error rather than compile time. On the plus side, all of the support functions may be called with ByVal parameters,
            // so the translated code is slightly more succinct that it would be if they had to support ByRef.
            var expressionToSet = new Expression(new IToken[]
            {
                new NameToken("a", 0)
            });
            var expressionToSetTo = new Expression(new IToken[]
            {
                new BuiltInFunctionToken("CDate", 0),
                new OpenBrace(0),
                new NameToken("a", 0),
                new ArgumentSeparatorToken(0),
                new NameToken("b", 0),
                new CloseBrace(0)
            });
            var expected = new TranslatedStatementContentDetails(
                "_env.a = _.VAL(_.CALL(this, _, \"CDATE\", _.ARGS.Val(_env.a).Val(_env.b)))",
                new NonNullImmutableList <NameToken>(new[] { new NameToken("a", 0), new NameToken("b", 0) })
                );
            var scopeAccessInformation = GetEmptyScopeAccessInformation();
            var actual = GetDefaultValueSettingStatementTranslator().Translate(
                new ValueSettingStatement(
                    expressionToSet,
                    expressionToSetTo,
                    ValueSettingStatement.ValueSetTypeOptions.Let
                    ),
                scopeAccessInformation
                );

            Assert.Equal(expected, actual, new TranslatedStatementContentDetailsComparer());
        }
        public void InvalidFunctionSettingMustCompileThoughFailAtRunTime()
        {
            var expressionToSet = new Expression(new IToken[]
            {
                new NameToken("a", 0),
                new OpenBrace(0),
                new NumericValueToken("1", 0),
                new CloseBrace(0)
            });
            var expressionToSetTo = new Expression(new[]
            {
                new NumericValueToken("1", 0)
            });
            var expected = new TranslatedStatementContentDetails(
                "_.SET((Int16)1, this, _.RAISEERROR(new IllegalAssignmentException(\"'a'\")), null, _.ARGS.Val((Int16)1))",
                new NonNullImmutableList <NameToken>(new[] { new NameToken("a", 0) })
                );
            var scopeAccessInformation = AddOutermostScopeFunction(
                GetEmptyScopeAccessInformation(),
                "a",
                0
                );
            var actual = GetDefaultValueSettingStatementTranslator().Translate(
                new ValueSettingStatement(
                    expressionToSet,
                    expressionToSetTo,
                    ValueSettingStatement.ValueSetTypeOptions.Let
                    ),
                scopeAccessInformation
                );

            Assert.Equal(expected, actual, new TranslatedStatementContentDetailsComparer());
        }
        public void BuiltInFunctionsAreMappedToTheSupportClassAndMayBeCalledDirectlyIfArgumentCountsMatch()
        {
            // CDate(..) needs to be mapped to _.CDATE(..) - this may be called directly if the correct number of arguments are specified. If an incorrect number
            // of arguments is passed then the support function must be executed via the "CALL" method (so that the error arises at runtime, rather than compile
            // time, in order to be consistent with VBScript), see BuiltInFunctionsAreMappedToTheSupportClassButMayNotBeCalledDirectlyIfArgumentCountsMatch.
            var expressionToSet = new Expression(new IToken[]
            {
                new NameToken("a", 0)
            });
            var expressionToSetTo = new Expression(new IToken[]
            {
                new BuiltInFunctionToken("CDate", 0),
                new OpenBrace(0),
                new NameToken("a", 0),
                new CloseBrace(0)
            });
            var expected = new TranslatedStatementContentDetails(
                "_env.a = _.CDATE(_env.a)",
                new NonNullImmutableList <NameToken>(new[] { new NameToken("a", 0) })
                );
            var scopeAccessInformation = GetEmptyScopeAccessInformation();
            var actual = GetDefaultValueSettingStatementTranslator().Translate(
                new ValueSettingStatement(
                    expressionToSet,
                    expressionToSetTo,
                    ValueSettingStatement.ValueSetTypeOptions.Let
                    ),
                scopeAccessInformation
                );

            Assert.Equal(expected, actual, new TranslatedStatementContentDetailsComparer());
        }
        public void OutermostScopeDeclaredSimpleValueTypeUpdate()
        {
            var expressionToSet = new Expression(new IToken[]
            {
                new NameToken("a", 0)
            });
            var expressionToSetTo = new Expression(new[]
            {
                new NumericValueToken("1", 0)
            });
            var expected = new TranslatedStatementContentDetails(
                "_outer.a = (Int16)1",
                new NonNullImmutableList <NameToken>(new[] { new NameToken("a", 0) })
                );
            var scopeAccessInformation = AddOutermostScopeVariable(
                GetEmptyScopeAccessInformation(),
                "a",
                0
                );
            var actual = GetDefaultValueSettingStatementTranslator().Translate(
                new ValueSettingStatement(
                    expressionToSet,
                    expressionToSetTo,
                    ValueSettingStatement.ValueSetTypeOptions.Let
                    ),
                scopeAccessInformation
                );

            Assert.Equal(expected, actual, new TranslatedStatementContentDetailsComparer());
        }
        public void UndeclaredSimpleValueTypeUpdateOfArray()
        {
            var expressionToSet = new Expression(new IToken[]
            {
                new NameToken("a", 0),
                new OpenBrace(0),
                new NumericValueToken("1", 0),
                new CloseBrace(0)
            });
            var expressionToSetTo = new Expression(new[]
            {
                new NumericValueToken("1", 0)
            });
            var expected = new TranslatedStatementContentDetails(
                "_.SET((Int16)1, this, _env.a, null, _.ARGS.Val((Int16)1))",
                new NonNullImmutableList <NameToken>(new[] { new NameToken("a", 0) })
                );
            var scopeAccessInformation = GetEmptyScopeAccessInformation();
            var actual = GetDefaultValueSettingStatementTranslator().Translate(
                new ValueSettingStatement(
                    expressionToSet,
                    expressionToSetTo,
                    ValueSettingStatement.ValueSetTypeOptions.Let
                    ),
                scopeAccessInformation
                );

            Assert.Equal(expected, actual, new TranslatedStatementContentDetailsComparer());
        }
Exemple #9
0
        public void NestedBracketAndFurtherMemberAccessExpressionShouldBePassedByValIntoFunctions()
        {
            // When "a.b(0).c" is considered as an argument, it should be identified as ByVal (since only a direct reference - eg. "a" or even "a(0)" if "a"
            // is an array - can be changed ByRef as a function argument). Before adding this test, there was an issue where "a.b(0).c" would throw an exception
            // during translation.
            var expression = new Expression(new[]
            {
                new CallSetExpressionSegment(new[]
                {
                    new CallSetItemExpressionSegment(
                        new[] { new NameToken("a", 0), new NameToken("b", 0) },
                        new[] { new Expression(new[] { new NumericValueExpressionSegment(new NumericValueToken("0", 0)) }) },
                        zeroArgumentBracketsPresence: null
                        ),
                    new CallSetItemExpressionSegment(
                        new[] { new NameToken("c", 0) },
                        new Expression[0],
                        CallSetItemExpressionSegment.ArgumentBracketPresenceOptions.Absent
                        )
                })
            });

            var expected = new TranslatedStatementContentDetails(
                "_.ARGS.Val(_.CALL(this, _.CALL(this, _env.a, \"b\", _.ARGS.Val((Int16)0)), \"c\"))",
                new NonNullImmutableList <NameToken>(new[] {
                new NameToken("a", 0)
            })
                );

            Assert.Equal(
                expected,
                GetDefaultStatementTranslator().TranslateAsArgumentProvider(new[] { expression }, GetEmptyScopeAccessInformation(), forceAllArgumentsToBeByVal: false),
                new TranslatedStatementContentDetailsComparer()
                );
        }
Exemple #10
0
        public void IsolatedFunctionCallAccordingToScopeDoesNotHaveValueTypeAccessLogic()
        {
            // "o" (where there is a function in scope called "o")
            var expression = new Expression(new[]
            {
                new CallExpressionSegment(
                    new[] { new NameToken("o", 0) },
                    new Expression[0],
                    CallExpressionSegment.ArgumentBracketPresenceOptions.Absent
                    )
            });

            var scopeAccessInformation = AddOutermostScopeFunction(
                GetEmptyScopeAccessInformation(),
                "o",
                0
                );
            var expected = new TranslatedStatementContentDetails(
                "_.CALL(this, _outer, \"o\")",
                new NonNullImmutableList <NameToken>(new[] { new NameToken("o", 0) })
                );

            Assert.Equal(
                expected,
                GetDefaultStatementTranslator().Translate(expression, scopeAccessInformation, ExpressionReturnTypeOptions.None),
                new TranslatedStatementContentDetailsComparer()
                );
        }
        public void UndeclaredSetTargetsWithinFunctionsAreScopeRestrictedToThatFunction()
        {
            // The ValueSettingStatementsTranslator wasn't using the ScopeAccessInformation's GetNameOfTargetContainerIfAnyRequired extension method and
            // was incorrectly applying the logic that it should have gotten for free by using that method - if an undeclared variable was being accessed
            // within a method (for the to-set target) then it was being mapped back to the "Environment References" class instead of being treated as
            // local to the function.
            var expressionToSet = new Expression(new IToken[]
            {
                new NameToken("a", 0)
            });
            var expressionToSetTo = new Expression(new IToken[]
            {
                new NumericValueToken("1", 0)
            });
            var valueSettingStatement = new ValueSettingStatement(
                expressionToSet,
                expressionToSetTo,
                ValueSettingStatement.ValueSetTypeOptions.Let
                );

            var containingFunction = new FunctionBlock(
                isPublic: true,
                isDefault: false,
                name: new NameToken("F1", 0),
                parameters: new AbstractFunctionBlock.Parameter[0],
                statements: new[] { valueSettingStatement }
                );

            var expected = new TranslatedStatementContentDetails(
                "a = (Int16)1",
                new NonNullImmutableList <NameToken>(new[] { new NameToken("a", 0) })
                );
            var scopeAccessInformation = GetEmptyScopeAccessInformation();

            scopeAccessInformation = new ScopeAccessInformation(
                containingFunction,   // parent
                containingFunction,   // scopeDefiningParent
                new CSharpName("F1"), // parentReturnValueName
                scopeAccessInformation.ErrorRegistrationTokenIfAny,
                scopeAccessInformation.DirectedWithReferenceIfAny,
                scopeAccessInformation.ExternalDependencies,
                scopeAccessInformation.Classes,
                scopeAccessInformation.Functions.Add(new ScopedNameToken("F1", 0, ScopeLocationOptions.WithinFunctionOrPropertyOrWith)),
                scopeAccessInformation.Properties,
                scopeAccessInformation.Constants,
                scopeAccessInformation.Variables,
                scopeAccessInformation.StructureExitPoints
                );
            var actual = GetDefaultValueSettingStatementTranslator().Translate(valueSettingStatement, scopeAccessInformation);

            Assert.Equal(expected, actual, new TranslatedStatementContentDetailsComparer());
        }
Exemple #12
0
        public void NestedFunctionOrArrayAccess()
        {
            // "a(0)(b)" (where neither a nor b are defined and so there could be method calls OR array accesses)
            var expression = new Expression(new[]
            {
                new CallSetExpressionSegment(new[]
                {
                    new CallSetItemExpressionSegment(
                        new[] { new NameToken("a", 0) },
                        new[] { new Expression(new[] { new NumericValueExpressionSegment(new NumericValueToken("0", 0)) }) },
                        null
                        ),
                    new CallSetItemExpressionSegment(
                        new IToken[0],
                        new[] { new Expression(new[] { new CallExpressionSegment(
                                                           new[] { new NameToken("b", 0) },
                                                           new Expression[0],
                                                           CallSetItemExpressionSegment.ArgumentBracketPresenceOptions.Absent
                                                           ) }) },
                        null
                        )
                })
            });

            // Since we can't know until runtime if "a" is an array that is being accessed or a function/property, the arguments need to
            // be constructed to work as ByVal or ByRef if it IS a function or property. Since "0" is a constant it will be ByVal but
            // since "b" is a variable it has to be marked as exligible for ByRef (this will not have any effect if "a(0)" is an
            // array or if it is an object with a default function or property whose argument is marked as ByVal, but we won't
            // know that until runtime).
            var expected = new TranslatedStatementContentDetails(
                "_.CALL(this, _.CALL(this, _env.a, _.ARGS.Val((Int16)0)), _.ARGS.Ref(_env.b, v0 => { _env.b = v0; }))",
                new NonNullImmutableList <NameToken>(new[] {
                new NameToken("a", 0),
                new NameToken("b", 0)
            })
                );

            Assert.Equal(
                expected,
                GetDefaultStatementTranslator().Translate(expression, GetEmptyScopeAccessInformation(), ExpressionReturnTypeOptions.None),
                new TranslatedStatementContentDetailsComparer()
                );
        }
Exemple #13
0
        public void KnownVariablePassedAsArgumentToKnownFunctionIsPassedByValIfWrappedInBrackets()
        {
            // "o((a))" (where there is a function in scope called "o" and a variable "a")
            var expression = new Expression(new[]
            {
                new CallExpressionSegment(
                    new[] { new NameToken("o", 0) },
                    new[]
                {
                    new Expression(new[] {
                        new BracketedExpressionSegment(new[] {
                            new CallExpressionSegment(new[] { new NameToken("a", 0) }, new Expression[0], CallSetItemExpressionSegment.ArgumentBracketPresenceOptions.Absent)
                        })
                    })
                },
                    null
                    )
            });

            var scopeAccessInformation = AddOutermostScopeVariable(
                AddOutermostScopeFunction(
                    GetEmptyScopeAccessInformation(),
                    "o",
                    0
                    ),
                "a",
                0
                );
            var expected = new TranslatedStatementContentDetails(
                "_.CALL(this, _outer, \"o\", _.ARGS.Val(_outer.a))",
                new NonNullImmutableList <NameToken>(new[] {
                new NameToken("a", 0),
                new NameToken("o", 0)
            })
                );

            Assert.Equal(
                expected,
                GetDefaultStatementTranslator().Translate(expression, scopeAccessInformation, ExpressionReturnTypeOptions.None),
                new TranslatedStatementContentDetailsComparer()
                );
        }
Exemple #14
0
        public void IsolatedNonFunctionOrPropertyReferenceHasValueTypeAccessLogic()
        {
            // "o" (where there is no function or property in scope called "o")
            var expression = new Expression(new[]
            {
                new CallExpressionSegment(
                    new[] { new NameToken("o", 0) },
                    new Expression[0],
                    CallExpressionSegment.ArgumentBracketPresenceOptions.Absent
                    )
            });
            var expected = new TranslatedStatementContentDetails(
                "_.VAL(_env.o)",
                new NonNullImmutableList <NameToken>(new[] { new NameToken("o", 0) })
                );
            var scopeAccessInformation = GetEmptyScopeAccessInformation();

            Assert.Equal(
                expected,
                GetDefaultStatementTranslator().Translate(expression, scopeAccessInformation, ExpressionReturnTypeOptions.None),
                new TranslatedStatementContentDetailsComparer()
                );
        }
        public TranslationResult Translate(IfBlock ifBlock, ScopeAccessInformation scopeAccessInformation, int indentationDepth)
        {
            if (ifBlock == null)
            {
                throw new ArgumentNullException("ifBlock");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (indentationDepth < 0)
            {
                throw new ArgumentOutOfRangeException("indentationDepth", "must be zero or greater");
            }

            // These TranslatedContent values are the content that will ultimately be forced into a boolean and used to construct an "if" conditional, it it C# code. If there is no
            // error -trapping to worry about then we just have to wrap this in an IF call (the "IF" method in the runtime support class) and be done with it. If error-trapping MAY
            // be involved, though, then it's more complicated - we might be able to just use the IF extension method that takes an error registration token as an argument (this is
            // the second least-complicated code path) but if we're within a function (or property) and the there any function or property calls within this translated content that
            // take by-ref arguments and any of those arguments are by-ref arguments of the containing function, then it will have to be re-written since C# will not allow "ref"
            // arguments to be manipulated in lambas (and that is how by-ref arguments are dealt with when calling nested functions or properties). This third arrangement is
            // the most complicated code path.
            var byRefArgumentIdentifier = new FuncByRefArgumentMapper(_nameRewriter, _tempNameGenerator, _logger);
            var conditionalClausesWithTranslatedConditions = ifBlock.ConditionalClauses
                                                             .Select((conditional, index) => new
            {
                Index             = index,
                Conditional       = conditional,
                TranslatedContent = _statementTranslator.Translate(
                    conditional.Condition,
                    scopeAccessInformation,
                    ExpressionReturnTypeOptions.NotSpecified,
                    _logger.Warning
                    ),
                ByRefArgumentsToRewriteInTranslatedContent = byRefArgumentIdentifier.GetByRefArgumentsThatNeedRewriting(
                    conditional.Condition.ToStageTwoParserExpression(scopeAccessInformation, ExpressionReturnTypeOptions.NotSpecified, _logger.Warning),
                    scopeAccessInformation,
                    new NonNullImmutableList <FuncByRefMapping>()
                    )
            })
                                                             .ToArray();

            var translationResult = TranslationResult.Empty;
            var numberOfAdditionalBlocksInjectedForErrorTrapping = 0;

            foreach (var conditionalEntry in conditionalClausesWithTranslatedConditions)
            {
                var conditionalContent       = conditionalEntry.TranslatedContent;
                var previousConditionalEntry = (conditionalEntry.Index == 0) ? null : conditionalClausesWithTranslatedConditions[conditionalEntry.Index - 1];

                // If we're dealing with multiple if (a).. elseif (b).. elseif (c).. [else] blocks then these would be most simply represented by if (a).. else if (b)..
                // else if (c).. [else] blocks in C#. However, if error-trapping is involved then some of the conditions may have to be rewritten to deal with by-ref arguments
                // and then (after the condition is evaluated) those rewritten arguments need to be pushed back on to the original references. In this case, each subsequent "if"
                // condition must be within its own "else" block in order for the the rewritten condition to be evaluated when required (and not before). When this happens, there
                // will be greater levels of nesting required. This nesting is injected here and tracked with the variable "numberOfAdditionalBlocksInjectedForErrorTrapping" (this
                // will be used further down to ensure that any extra levels of nesting are closed off).
                bool requiresNewScopeWithinElseBlock;
                if (previousConditionalEntry == null)
                {
                    requiresNewScopeWithinElseBlock = false;
                }
                else
                {
                    requiresNewScopeWithinElseBlock = previousConditionalEntry.ByRefArgumentsToRewriteInTranslatedContent.Any() || conditionalEntry.ByRefArgumentsToRewriteInTranslatedContent.Any();
                }
                if (requiresNewScopeWithinElseBlock)
                {
                    translationResult = translationResult.Add(new TranslatedStatement("else", indentationDepth, translationResult.TranslatedStatements.Last().LineIndexOfStatementStartInSource));
                    translationResult = translationResult.Add(new TranslatedStatement("{", indentationDepth, translationResult.TranslatedStatements.Last().LineIndexOfStatementStartInSource));
                    indentationDepth++;
                    numberOfAdditionalBlocksInjectedForErrorTrapping++;
                }

                // Check whether there are any "ref" arguments that need rewriting - this is only applicable if we're within a function or property that has ByRef arguments.
                // If this is the case then we need to ensure that we do not emit code that tries to include those references within a lambda since that is not valid C#. One
                // way in which this may occur is the passing of a "ref" argument into another function as ByRef as part of the condition evaluation (since the argument provider
                // will update the value after the call completes using a lambda). The other way is if error-trapping might be enabled at runtime - in this case, the evaluation
                // of the condition is performed within a lambda so that any errors can be swallowed if necessary. If any such reference rewriting is required then the code that
                // must be emitted is more complex.
                var byRefArgumentsToRewrite = conditionalEntry.ByRefArgumentsToRewriteInTranslatedContent;
                if (byRefArgumentsToRewrite.Any())
                {
                    // If we're in a function or property and that function / property has by-ref arguments that we then need to pass into further function / property calls
                    // in order to evaluate the current conditional, then we need to record those values in temporary references, try to evaulate the condition and then push
                    // the temporary values back into the original references. This is required in order to be consistent with VBScript and yet also produce code that compiles
                    // as C# (which will not let "ref" arguments of the containing function be used in lambdas, which is how we deal with updating by-ref arguments after function
                    // or property calls complete).
                    var evaluatedResultName = _tempNameGenerator(new CSharpName("ifResult"), scopeAccessInformation);
                    scopeAccessInformation = scopeAccessInformation.ExtendVariables(
                        byRefArgumentsToRewrite
                        .Select(r => new ScopedNameToken(r.To.Name, r.From.LineIndex, ScopeLocationOptions.WithinFunctionOrPropertyOrWith))
                        .ToNonNullImmutableList()
                        );
                    translationResult = translationResult.Add(new TranslatedStatement(
                                                                  "bool " + evaluatedResultName.Name + ";",
                                                                  indentationDepth,
                                                                  conditionalEntry.Conditional.Condition.Tokens.First().LineIndex
                                                                  ));
                    var byRefMappingOpeningTranslationDetails = byRefArgumentsToRewrite.OpenByRefReplacementDefinitionWork(translationResult, indentationDepth, _nameRewriter);
                    translationResult = byRefMappingOpeningTranslationDetails.TranslationResult;
                    indentationDepth += byRefMappingOpeningTranslationDetails.DistanceToIndentCodeWithMappedValues;
                    var rewrittenConditionalContent = _statementTranslator.Translate(
                        byRefArgumentsToRewrite.RewriteExpressionUsingByRefArgumentMappings(conditionalEntry.Conditional.Condition, _nameRewriter),
                        scopeAccessInformation,
                        ExpressionReturnTypeOptions.NotSpecified,
                        _logger.Warning
                        );
                    var ifStatementFormat = (scopeAccessInformation.ErrorRegistrationTokenIfAny == null)
                                                ? "{0} = {1}.IF({2});"
                                                : "{0} = {1}.IF(() => {2}, {3});";
                    translationResult = translationResult.Add(new TranslatedStatement(
                                                                  string.Format(
                                                                      ifStatementFormat,
                                                                      evaluatedResultName.Name,
                                                                      _supportRefName.Name,
                                                                      rewrittenConditionalContent.TranslatedContent,
                                                                      (scopeAccessInformation.ErrorRegistrationTokenIfAny == null) ? "" : scopeAccessInformation.ErrorRegistrationTokenIfAny.Name
                                                                      ),
                                                                  indentationDepth,
                                                                  conditionalEntry.Conditional.Condition.Tokens.First().LineIndex
                                                                  ));
                    indentationDepth  -= byRefMappingOpeningTranslationDetails.DistanceToIndentCodeWithMappedValues;
                    translationResult  = byRefArgumentsToRewrite.CloseByRefReplacementDefinitionWork(translationResult, indentationDepth, _nameRewriter);
                    conditionalContent = new TranslatedStatementContentDetails(
                        evaluatedResultName.Name,
                        rewrittenConditionalContent.VariablesAccessed
                        );
                }
                else if (scopeAccessInformation.MayRequireErrorWrapping(ifBlock))
                {
                    // If we're not in a function or property or if that function or property does not have any by-ref arguments that we need to pass in as by-ref arguments
                    // to further functions or properties, then we're in the less complicate error-trapping scenario; we only have to use the IF extension method that deals
                    // with error-trapping.
                    conditionalContent = new TranslatedStatementContentDetails(
                        string.Format(
                            "{0}.IF(() => {1}, {2})",
                            _supportRefName.Name,
                            conditionalContent.TranslatedContent,
                            scopeAccessInformation.ErrorRegistrationTokenIfAny.Name
                            ),
                        conditionalContent.VariablesAccessed
                        );
                }
                else
                {
                    conditionalContent = new TranslatedStatementContentDetails(
                        string.Format(
                            "{0}.IF({1})",
                            _supportRefName.Name,
                            conditionalContent.TranslatedContent
                            ),
                        conditionalContent.VariablesAccessed
                        );
                }

                var undeclaredVariablesAccessed = conditionalContent.GetUndeclaredVariablesAccessed(scopeAccessInformation, _nameRewriter);
                foreach (var undeclaredVariable in undeclaredVariablesAccessed)
                {
                    _logger.Warning("Undeclared variable: \"" + undeclaredVariable.Content + "\" (line " + (undeclaredVariable.LineIndex + 1) + ")");
                }
                translationResult = translationResult.AddUndeclaredVariables(undeclaredVariablesAccessed);
                var innerStatements = conditionalEntry.Conditional.Statements.ToNonNullImmutableList();
                var conditionalInlineCommentIfAny = !innerStatements.Any() ? null : (innerStatements.First() as InlineCommentStatement);
                if (conditionalInlineCommentIfAny != null)
                {
                    innerStatements = innerStatements.RemoveAt(0);
                }
                translationResult = translationResult.Add(
                    new TranslatedStatement(
                        string.Format(
                            "{0} ({1}){2}",
                            (previousConditionalEntry == null) || requiresNewScopeWithinElseBlock ? "if" : "else if",
                            conditionalContent.TranslatedContent,
                            (conditionalInlineCommentIfAny == null) ? "" : (" //" + conditionalInlineCommentIfAny.Content)
                            ),
                        indentationDepth,
                        conditionalEntry.Conditional.Condition.Tokens.First().LineIndex
                        )
                    );
                translationResult = translationResult.Add(new TranslatedStatement("{", indentationDepth, conditionalEntry.Conditional.Condition.Tokens.First().LineIndex));
                translationResult = translationResult.Add(
                    Translate(
                        innerStatements,
                        scopeAccessInformation.SetParent(ifBlock),
                        indentationDepth + 1
                        )
                    );
                translationResult = translationResult.Add(new TranslatedStatement("}", indentationDepth, translationResult.TranslatedStatements.Last().LineIndexOfStatementStartInSource));
            }

            if (ifBlock.OptionalElseClause != null)
            {
                // Unlike the IF or ELSE IF lines, we don't have a LineIndex for the final ELSE block, so we'll just use the LineIndex of the previous line (we know that there
                // is one since it's not valid for an IfBlock to have ONLY an ELSE, it must have at least one IF before if). Note: We only have a LineIndex for the IF and ELSE
                // IF lines since those lines have conditions, which have tokens, and we use the LineIndex of the first token.
                translationResult = translationResult.Add(new TranslatedStatement("else", indentationDepth, translationResult.TranslatedStatements.Last().LineIndexOfStatementStartInSource));
                translationResult = translationResult.Add(new TranslatedStatement("{", indentationDepth, translationResult.TranslatedStatements.Last().LineIndexOfStatementStartInSource));
                translationResult = translationResult.Add(
                    Translate(
                        ifBlock.OptionalElseClause.Statements.ToNonNullImmutableList(),
                        scopeAccessInformation.SetParent(ifBlock),
                        indentationDepth + 1
                        )
                    );
                translationResult = translationResult.Add(new TranslatedStatement("}", indentationDepth, translationResult.TranslatedStatements.Last().LineIndexOfStatementStartInSource));
            }

            // If any additional levels of nesting were required above (for error-trapping scenarios), ensure they are closed off here
            for (var index = 0; index < numberOfAdditionalBlocksInjectedForErrorTrapping; index++)
            {
                indentationDepth--;
                translationResult = translationResult.Add(new TranslatedStatement("}", indentationDepth, translationResult.TranslatedStatements.Last().LineIndexOfStatementStartInSource));
            }

            return(translationResult);
        }
Exemple #16
0
        public TranslationResult Translate(DoBlock doBlock, ScopeAccessInformation scopeAccessInformation, int indentationDepth)
        {
            if (doBlock == null)
            {
                throw new ArgumentNullException("doBlock");
            }
            if (scopeAccessInformation == null)
            {
                throw new ArgumentNullException("scopeAccessInformation");
            }
            if (indentationDepth < 0)
            {
                throw new ArgumentOutOfRangeException("indentationDepth", "must be zero or greater");
            }

            if ((doBlock.ConditionIfAny == null) && !doBlock.Statements.Any())
            {
                _logger.Warning("Infinite DO/WHILE loop at line " + (doBlock.LineIndexOfStartOfConstruct + 1));
                return(TranslationResult.Empty.Add(new TranslatedStatement(
                                                       "while (true) { }",
                                                       indentationDepth,
                                                       doBlock.LineIndexOfStartOfConstruct
                                                       )));
            }

            var earlyExitNameIfAny = GetEarlyExitNameIfRequired(doBlock, scopeAccessInformation);
            var loopStatementsTranslationResult = Translate(
                doBlock.Statements.ToNonNullImmutableList(),
                scopeAccessInformation.SetParent(doBlock),
                doBlock.SupportsExit,
                earlyExitNameIfAny,
                indentationDepth + 1
                );

            TranslatedStatementContentDetails whileConditionExpressionContentIfAny;

            if (doBlock.ConditionIfAny == null)
            {
                whileConditionExpressionContentIfAny = null;
            }
            else
            {
                whileConditionExpressionContentIfAny = _statementTranslator.Translate(
                    doBlock.ConditionIfAny,
                    scopeAccessInformation,
                    ExpressionReturnTypeOptions.Boolean,
                    _logger.Warning
                    );
                if (!doBlock.IsDoWhileCondition)
                {
                    // C# doesn't support "DO UNTIL x" but it's equivalent to "DO WHILE !x"
                    whileConditionExpressionContentIfAny = new TranslatedStatementContentDetails(
                        "!" + whileConditionExpressionContentIfAny.TranslatedContent,
                        whileConditionExpressionContentIfAny.VariablesAccessed
                        );
                }
                if (scopeAccessInformation.ErrorRegistrationTokenIfAny != null)
                {
                    // Ensure that the frankly ludicrous VBScript error-handling is applied where required. As the IProvideVBScriptCompatFunctionality's IF method
                    // signature describes, if an error occurs in retrieving the value, it will be evaluated as true. So, given a function
                    //
                    //   FUNCTION GetValue()
                    //     Err.Raise vbObjectError, "Test", "Test"
                    //   END FUNCTION
                    //
                    // both of the following loops will be entered:
                    //
                    //   ON ERROR RESUME NEXT
                    //   DO WHILE GetValue()
                    //     WScript.Echo "True"
                    //     EXIT DO
                    //   LOOP
                    //
                    //   ON ERROR RESUME NEXT
                    //   DO UNTIL GetValue()
                    //     WScript.Echo "True"
                    //     EXIT DO
                    //   LOOP
                    //
                    // This is why an additional IF call must be wrapped around the IF above - since the negation of a DO UNTIL (as opposed to a DO WHILE) loop
                    // must be within the outer, error-handling IF call. (A simpler example of the above is to replace the GetValue() call with 1/0, which will
                    // result in a "Division by zero" error if ON ERROR RESUME NEXT is not present, but which will result in both of the above loops being
                    // entered if it IS present).
                    whileConditionExpressionContentIfAny = new TranslatedStatementContentDetails(
                        string.Format(
                            "{0}.IF(() => {1}, {2})",
                            _supportRefName.Name,
                            whileConditionExpressionContentIfAny.TranslatedContent,
                            scopeAccessInformation.ErrorRegistrationTokenIfAny.Name
                            ),
                        whileConditionExpressionContentIfAny.VariablesAccessed
                        );
                }
            }

            var translationResult = TranslationResult.Empty;

            if (whileConditionExpressionContentIfAny != null)
            {
                translationResult = translationResult.AddUndeclaredVariables(
                    whileConditionExpressionContentIfAny.GetUndeclaredVariablesAccessed(scopeAccessInformation, _nameRewriter)
                    );
            }

            if (whileConditionExpressionContentIfAny == null)
            {
                translationResult = translationResult.Add(new TranslatedStatement(
                                                              "while (true)",
                                                              indentationDepth,
                                                              doBlock.LineIndexOfStartOfConstruct
                                                              ));
            }
            else if (doBlock.IsPreCondition)
            {
                translationResult = translationResult.Add(new TranslatedStatement(
                                                              "while (" + whileConditionExpressionContentIfAny.TranslatedContent + ")",
                                                              indentationDepth,
                                                              doBlock.LineIndexOfStartOfConstruct
                                                              ));
            }
            else
            {
                translationResult = translationResult.Add(new TranslatedStatement(
                                                              "do",
                                                              indentationDepth,
                                                              doBlock.LineIndexOfStartOfConstruct
                                                              ));
            }
            translationResult = translationResult.Add(new TranslatedStatement("{", indentationDepth, doBlock.LineIndexOfStartOfConstruct));
            if (earlyExitNameIfAny != null)
            {
                translationResult = translationResult.Add(new TranslatedStatement(
                                                              string.Format("var {0} = false;", earlyExitNameIfAny.Name),
                                                              indentationDepth + 1,
                                                              doBlock.LineIndexOfStartOfConstruct
                                                              ));
            }
            translationResult = translationResult.Add(loopStatementsTranslationResult);
            var lineIndexForClosingCode = loopStatementsTranslationResult.TranslatedStatements.Any()
                                ? loopStatementsTranslationResult.TranslatedStatements.Last().LineIndexOfStatementStartInSource
                                : doBlock.LineIndexOfStartOfConstruct;

            if ((whileConditionExpressionContentIfAny == null) || doBlock.IsPreCondition)
            {
                translationResult = translationResult.Add(new TranslatedStatement("}", indentationDepth, lineIndexForClosingCode));
            }
            else
            {
                translationResult = translationResult.Add(new TranslatedStatement(
                                                              "} while (" + whileConditionExpressionContentIfAny.TranslatedContent + ");",
                                                              indentationDepth,
                                                              doBlock.ConditionIfAny.Tokens.First().LineIndex
                                                              ));
            }
            var earlyExitFlagNamesToCheck = scopeAccessInformation.StructureExitPoints
                                            .Where(e => e.ExitEarlyBooleanNameIfAny != null)
                                            .Select(e => e.ExitEarlyBooleanNameIfAny.Name);

            if (earlyExitFlagNamesToCheck.Any())
            {
                // These lines do not directly have equivalents in the source, so just take the line index of the previous line that was generated
                // by the above code
                var lineIndexForEarlyExitCode = translationResult.TranslatedStatements.Last().LineIndexOfStatementStartInSource;

                // Perform early-exit checks for any scopeAccessInformation.StructureExitPoints - if this is DO..LOOP loop inside a FOR loop and an
                // EXIT FOR was encountered within the DO..LOOP that must refer to the containing FOR, then the DO..LOOP will have been broken out
                // of, but also a flag set that means that we must break further to get out of the FOR loop.
                translationResult = translationResult
                                    .Add(new TranslatedStatement(
                                             "if (" + string.Join(" || ", earlyExitFlagNamesToCheck) + ")",
                                             indentationDepth,
                                             lineIndexForEarlyExitCode
                                             ))
                                    .Add(new TranslatedStatement(
                                             "break;",
                                             indentationDepth + 1,
                                             lineIndexForEarlyExitCode
                                             ));
            }
            return(translationResult);
        }