private bool TryReplaceBannedFunction()

in src/Bicep.Decompiler/TemplateConverter.cs [226:491]


        private bool TryReplaceBannedFunction(FunctionExpression expression, [NotNullWhen(true)] out SyntaxBase? syntax)
        {
            if (SyntaxHelpers.TryGetBinaryOperatorReplacement(expression.Function) is TokenType binaryTokenType)
            {
                var binaryOperator = Operators.TokenTypeToBinaryOperator[binaryTokenType];
                switch (binaryOperator)
                {
                    // ARM actually allows >= 2 args for and(), or() and coalesce()
                    case BinaryOperator.LogicalAnd:
                    case BinaryOperator.LogicalOr:
                    case BinaryOperator.Coalesce:
                        if (expression.Parameters.Length < 2)
                        {
                            throw new ArgumentException($"Expected a minimum of 2 parameters for function {expression.Function}");
                        }
                        break;
                    default:
                        if (expression.Parameters.Length != 2)
                        {
                            throw new ArgumentException($"Expected 2 parameters for binary function {expression.Function}");
                        }
                        break;
                }

                if (expression.Properties.Any())
                {
                    throw new ArgumentException($"Expected 0 properties for binary function {expression.Function}");
                }

                var leftParameter = expression.Parameters[0];
                var rightParameter = expression.Parameters[1];
                // if token is = or != check to see if they are insensitive conditions i.e =~ or !~
                if (binaryTokenType is TokenType.Equals || binaryTokenType is TokenType.NotEquals)
                {
                    if (leftParameter is FunctionExpression leftFunctionExpression &&
                        rightParameter is FunctionExpression rightFunctionExpression &&
                        leftFunctionExpression.Function == "toLower" &&
                        rightFunctionExpression.Function == "toLower")
                    {
                        leftParameter = leftFunctionExpression.Parameters[0];
                        rightParameter = rightFunctionExpression.Parameters[0];
                        binaryTokenType = binaryTokenType == TokenType.Equals ? TokenType.EqualsInsensitive : TokenType.NotEqualsInsensitive;
                        binaryOperator = Operators.TokenTypeToBinaryOperator[binaryTokenType];
                    }
                }

                var binaryOperation = new BinaryOperationSyntax(
                    ParseLanguageExpression(leftParameter),
                    SyntaxFactory.CreateToken(binaryTokenType),
                    ParseLanguageExpression(rightParameter));

                foreach (var parameter in expression.Parameters.Skip(2))
                {
                    binaryOperation = new BinaryOperationSyntax(
                        binaryOperation,
                        SyntaxFactory.CreateToken(binaryTokenType),
                        ParseLanguageExpression(parameter));
                }

                syntax = new ParenthesizedExpressionSyntax(
                    SyntaxFactory.LeftParenToken,
                    binaryOperation,
                    SyntaxFactory.RightParenToken);
                return true;
            }

            if (SyntaxHelpers.TryGetEmptyFunctionKeywordReplacement(expression.Function) is TokenType keywordTokenType)
            {
                if (expression.Parameters.Any())
                {
                    throw new ArgumentException($"Expected 0 parameters for function {expression.Function}");
                }

                if (expression.Properties.Any())
                {
                    throw new ArgumentException($"Expected 0 properties for function {expression.Function}");
                }

                syntax = SyntaxFactory.CreateToken(keywordTokenType);
                return true;
            }

            if (expression.IsNamed("not"))
            {
                if (expression.Parameters.Length != 1)
                {
                    throw new ArgumentException($"Expected 1 parameters for unary function {expression.Function}");
                }

                if (expression.Properties.Any())
                {
                    throw new ArgumentException($"Expected 0 properties for unary function {expression.Function}");
                }

                // Check to see if the inner expression is also a function and if it is equals we can
                // simplify the expression from (!(a == b)) to (a != b)
                if (expression.Parameters[0] is FunctionExpression functionExpression)
                {
                    if (functionExpression.IsNamed("equals"))
                    {
                        return TryReplaceBannedFunction(
                            new FunctionExpression("notEquals", functionExpression.Parameters, functionExpression.Properties),
                        out syntax);
                    }
                }

                syntax = new ParenthesizedExpressionSyntax(
                    SyntaxFactory.LeftParenToken,
                    new UnaryOperationSyntax(
                        SyntaxFactory.ExclamationToken,
                        ParseLanguageExpression(expression.Parameters[0])),
                    SyntaxFactory.RightParenToken);
                return true;
            }

            if (expression.IsNamed("if"))
            {
                if (expression.Parameters.Length != 3)
                {
                    throw new ArgumentException($"Expected 3 parameters for ternary function {expression.Function}");
                }

                if (expression.Properties.Any())
                {
                    throw new ArgumentException($"Expected 0 properties for ternary function {expression.Function}");
                }

                syntax = new ParenthesizedExpressionSyntax(
                    SyntaxFactory.LeftParenToken,
                    new TernaryOperationSyntax(
                        ParseLanguageExpression(expression.Parameters[0]),
                        [],
                        SyntaxFactory.QuestionToken,
                        ParseLanguageExpression(expression.Parameters[1]),
                        [],
                        SyntaxFactory.ColonToken,
                        ParseLanguageExpression(expression.Parameters[2])),
                    SyntaxFactory.RightParenToken);
                return true;
            }

            if (expression.IsNamed("createArray"))
            {
                syntax = SyntaxFactory.CreateArray(expression.Parameters.Select(ParseLanguageExpression));
                return true;
            }

            if (expression.IsNamed("createObject"))
            {
                syntax = SyntaxFactory.CreateObject(expression.Parameters.Select(ParseLanguageExpression).Chunk(2).Select(pair =>
                {
                    if (pair[0] is StringSyntax keyString)
                    {
                        var keyLiteral = keyString.TryGetLiteralValue();
                        if (keyLiteral is not null)
                        {
                            return SyntaxFactory.CreateObjectProperty(keyLiteral, pair[1]);
                        }
                        else
                        {
                            // key is an interpolated string
                            return new ObjectPropertySyntax(pair[0], SyntaxFactory.ColonToken, pair[1]);
                        }
                    }

                    // key is a non-string expression
                    // since ObjectPropertySyntax only accepts IdentifierSyntax or StringSyntax, we need
                    // to wrap it in a string syntax
                    var keySyntax = SyntaxFactory.CreateString(new[] { string.Empty, string.Empty }, pair[0].AsEnumerable());
                    return new ObjectPropertySyntax(keySyntax, SyntaxFactory.ColonToken, pair[1]);
                }));
                return true;
            }

            if (StringComparer.OrdinalIgnoreCase.Equals(expression.Function, "lambda"))
            {
                if (expression.Parameters.Length < 1)
                {
                    throw new ArgumentException($"Expected at least 1 argument for function {expression.Function}");
                }

                var lambdaExpression = expression.Parameters.Last();
                var paramNames = new string[expression.Parameters.Length - 1];
                for (var i = 0; i < expression.Parameters.Length - 1; i++)
                {
                    if (expression.Parameters[i] is not JTokenExpression jtokenParam ||
                        jtokenParam.Value.Type is not JTokenType.String)
                    {
                        throw new ArgumentException($"Argument {i} for {expression.Function} is not a valid identifier");
                    }

                    paramNames[i] = jtokenParam.Value.ToString();
                }

                syntax = SyntaxFactory.CreateLambdaSyntax(paramNames, ParseLanguageExpression(lambdaExpression));
                return true;
            }

            if (StringComparer.OrdinalIgnoreCase.Equals(expression.Function, "lambdaVariables"))
            {
                if (expression.Parameters.Length != 1 ||
                    expression.Parameters[0] is not JTokenExpression jtokenParam ||
                    jtokenParam.Value.Type is not JTokenType.String)
                {
                    throw new ArgumentException($"Expected exactly 1 parameter of type {JTokenType.String} for function {expression.Function}");
                }

                syntax = SyntaxFactory.CreateIdentifier(jtokenParam.Value.ToString());
                return true;
            }

            if (expression.IsNamed("tryGet"))
            {
                if (expression.Parameters.Length < 2)
                {
                    throw new ArgumentException($"Expected at least 2 parameters for function {expression.Function}");
                }

                syntax = ParsePropertyAccess(ParseLanguageExpression(expression.Parameters[0]), expression.Parameters[1], safeNavigation: true);
                for (int i = 2; i < expression.Parameters.Length; i++)
                {
                    syntax = ParsePropertyAccess(syntax, expression.Parameters[i], safeNavigation: false, allowWrappedProperties: true);
                }

                if (expression.Properties.Length > 0)
                {
                    syntax = new ParenthesizedExpressionSyntax(SyntaxFactory.LeftParenToken, syntax, SyntaxFactory.RightParenToken);
                }

                return true;
            }

            if (expression.IsNamed("indexFromEnd"))
            {
                if (expression.Parameters.Length != 2)
                {
                    throw new ArgumentException($"Expected exactly 2 parameters for function {expression.Function}");
                }

                syntax = ParsePropertyAccess(ParseLanguageExpression(expression.Parameters[0]), expression.Parameters[1], safeNavigation: false, fromEnd: true);
                return true;
            }

            if (expression.IsNamed("tryIndexFromEnd"))
            {
                if (expression.Parameters.Length < 2)
                {
                    throw new ArgumentException($"Expected at least 2 parameters for function {expression.Function}");
                }

                syntax = ParsePropertyAccess(ParseLanguageExpression(expression.Parameters[0]), expression.Parameters[1], safeNavigation: true, fromEnd: true);
                for (int i = 2; i < expression.Parameters.Length; i++)
                {
                    syntax = ParsePropertyAccess(syntax, expression.Parameters[i], safeNavigation: false, allowWrappedProperties: true);
                }

                if (expression.Properties.Length > 0)
                {
                    syntax = new ParenthesizedExpressionSyntax(SyntaxFactory.LeftParenToken, syntax, SyntaxFactory.RightParenToken);
                }
                return true;
            }

            syntax = null;
            return false;
        }