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}"); } }
/// <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}"); } }
/// <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}"); } }
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}'.")); }