Пример #1
0
        public void EmitExpression(SyntaxBase syntax)
        {
            switch (syntax)
            {
            case BooleanLiteralSyntax boolSyntax:
                writer.WriteValue(boolSyntax.Value);
                break;

            case NumericLiteralSyntax numericSyntax:
                writer.WriteValue(numericSyntax.Value);
                break;

            case NullLiteralSyntax _:
                writer.WriteNull();

                break;

            case ObjectSyntax objectSyntax:
                writer.WriteStartObject();
                EmitObjectProperties(objectSyntax);
                writer.WriteEndObject();

                break;

            case ArraySyntax arraySyntax:
                writer.WriteStartArray();

                foreach (ArrayItemSyntax itemSyntax in arraySyntax.Items)
                {
                    EmitExpression(itemSyntax.Value);
                }

                writer.WriteEndArray();

                break;

            case ParenthesizedExpressionSyntax _:
            case UnaryOperationSyntax _:
            case BinaryOperationSyntax _:
            case TernaryOperationSyntax _:
            case StringSyntax _:
            case InstanceFunctionCallSyntax _:
            case FunctionCallSyntax _:
            case ArrayAccessSyntax _:
            case PropertyAccessSyntax _:
            case VariableAccessSyntax _:
                EmitLanguageExpression(syntax);

                break;

            default:
                throw new NotImplementedException($"Cannot emit unexpected expression of type {syntax.GetType().Name}");
            }
        }
Пример #2
0
        /// <summary>
        /// Converts the specified bicep expression tree into an ARM template expression tree.
        /// The returned tree may be rooted at either a function expression or jtoken expression.
        /// </summary>
        /// <param name="expression">The expression</param>
        public LanguageExpression ConvertExpression(SyntaxBase expression)
        {
            switch (expression)
            {
            case BooleanLiteralSyntax boolSyntax:
                return(CreateFunction(boolSyntax.Value ? "true" : "false"));

            case NumericLiteralSyntax numericSyntax:
                return(new JTokenExpression(numericSyntax.Value));

            case StringSyntax stringSyntax:
                // using the throwing method to get semantic value of the string because
                // error checking should have caught any errors by now
                return(ConvertString(stringSyntax));

            case NullLiteralSyntax _:
                return(CreateFunction("null"));

            case ObjectSyntax @object:
                return(ConvertObject(@object));

            case ArraySyntax array:
                return(ConvertArray(array));

            case ParenthesizedExpressionSyntax parenthesized:
                // template expressions do not have operators so parentheses are irrelevant
                return(ConvertExpression(parenthesized.Expression));

            case UnaryOperationSyntax unary:
                return(ConvertUnary(unary));

            case BinaryOperationSyntax binary:
                return(ConvertBinary(binary));

            case TernaryOperationSyntax ternary:
                return(CreateFunction(
                           "if",
                           ConvertExpression(ternary.ConditionExpression),
                           ConvertExpression(ternary.TrueExpression),
                           ConvertExpression(ternary.FalseExpression)));

            case FunctionCallSyntax function:
                return(ConvertFunction(
                           function.Name.IdentifierName,
                           function.Arguments.Select(a => ConvertExpression(a.Expression))));

            case InstanceFunctionCallSyntax instanceFunctionCall:
                var namespaceSymbol = context.SemanticModel.GetSymbolInfo(instanceFunctionCall.BaseExpression);
                Assert(namespaceSymbol is NamespaceSymbol, $"BaseExpression must be a NamespaceSymbol, instead got: '{namespaceSymbol?.Kind}'");

                return(ConvertFunction(
                           instanceFunctionCall.Name.IdentifierName,
                           instanceFunctionCall.Arguments.Select(a => ConvertExpression(a.Expression))));

            case ArrayAccessSyntax arrayAccess:
                return(AppendProperties(
                           ToFunctionExpression(arrayAccess.BaseExpression),
                           ConvertExpression(arrayAccess.IndexExpression)));

            case PropertyAccessSyntax propertyAccess:
                if (propertyAccess.BaseExpression is VariableAccessSyntax propVariableAccess &&
                    context.SemanticModel.GetSymbolInfo(propVariableAccess) is ResourceSymbol resourceSymbol)
                {
                    // special cases for certain resource property access. if we recurse normally, we'll end up
                    // generating statements like reference(resourceId(...)).id which are not accepted by ARM

                    var typeReference = EmitHelpers.GetTypeReference(resourceSymbol);
                    switch (propertyAccess.PropertyName.IdentifierName)
                    {
                    case "id":
                        return(GetLocallyScopedResourceId(resourceSymbol));

                    case "name":
                        return(GetResourceNameExpression(resourceSymbol));

                    case "type":
                        return(new JTokenExpression(typeReference.FullyQualifiedType));

                    case "apiVersion":
                        return(new JTokenExpression(typeReference.ApiVersion));

                    case "properties":
                        // use the reference() overload without "full" to generate a shorter expression
                        return(GetReferenceExpression(resourceSymbol, typeReference, false));
                    }
                }

                var moduleAccess = TryGetModulePropertyAccess(propertyAccess);
                if (moduleAccess != null)
                {
                    var(moduleSymbol, outputName) = moduleAccess.Value;
                    return(AppendProperties(
                               GetModuleOutputsReferenceExpression(moduleSymbol),
                               new JTokenExpression(outputName),
                               new JTokenExpression("value")));
                }

                return(AppendProperties(
                           ToFunctionExpression(propertyAccess.BaseExpression),
                           new JTokenExpression(propertyAccess.PropertyName.IdentifierName)));

            case VariableAccessSyntax variableAccess:
                return(ConvertVariableAccess(variableAccess));

            default:
                throw new NotImplementedException($"Cannot emit unexpected expression of type {expression.GetType().Name}");
            }
        }
Пример #3
0
        /// <summary>
        /// Converts the specified bicep expression tree into an ARM template expression tree.
        /// The returned tree may be rooted at either a function expression or jtoken expression.
        /// </summary>
        /// <param name="expression">The expression</param>
        public LanguageExpression ConvertExpression(SyntaxBase expression)
        {
            switch (expression)
            {
            case BooleanLiteralSyntax boolSyntax:
                return(CreateFunction(boolSyntax.Value ? "true" : "false"));

            case IntegerLiteralSyntax integerSyntax:
                return(integerSyntax.Value > int.MaxValue || integerSyntax.Value < int.MinValue ? CreateFunction("json", new JTokenExpression(integerSyntax.Value.ToInvariantString())) : new JTokenExpression((int)integerSyntax.Value));

            case StringSyntax stringSyntax:
                // using the throwing method to get semantic value of the string because
                // error checking should have caught any errors by now
                return(ConvertString(stringSyntax));

            case NullLiteralSyntax _:
                return(CreateFunction("null"));

            case ObjectSyntax @object:
                return(ConvertObject(@object));

            case ArraySyntax array:
                return(ConvertArray(array));

            case ParenthesizedExpressionSyntax parenthesized:
                // template expressions do not have operators so parentheses are irrelevant
                return(ConvertExpression(parenthesized.Expression));

            case UnaryOperationSyntax unary:
                return(ConvertUnary(unary));

            case BinaryOperationSyntax binary:
                return(ConvertBinary(binary));

            case TernaryOperationSyntax ternary:
                return(CreateFunction(
                           "if",
                           ConvertExpression(ternary.ConditionExpression),
                           ConvertExpression(ternary.TrueExpression),
                           ConvertExpression(ternary.FalseExpression)));

            case FunctionCallSyntaxBase functionCall:
                return(ConvertFunction(functionCall));

            case ArrayAccessSyntax arrayAccess:
                return(ConvertArrayAccess(arrayAccess));

            case ResourceAccessSyntax resourceAccess:
                return(ConvertResourceAccess(resourceAccess));

            case PropertyAccessSyntax propertyAccess:
                return(ConvertPropertyAccess(propertyAccess));

            case VariableAccessSyntax variableAccess:
                return(ConvertVariableAccess(variableAccess));

            default:
                throw new NotImplementedException($"Cannot emit unexpected expression of type {expression.GetType().Name}");
            }
        }
Пример #4
0
 private Symbol ResolveSymbol(SyntaxBase syntax)
 {
     // the binder guarantees that all bindable syntax nodes have a symbol attached to them even if it's the error symbol
     // if we see a null here, we have a code defect somewhere
     return(this.bindings.TryGetValue(syntax) ?? throw new InvalidOperationException($"Name binding failed to assign a symbol to syntax node of type '{syntax.GetType().Name}'."));
 }