in src/Bicep.Core/Emit/ExpressionConverter.cs [54:215]
public LanguageExpression ConvertExpression(Expression expression)
{
switch (expression)
{
case BooleanLiteralExpression @bool:
return CreateFunction(@bool.Value ? "true" : "false");
case IntegerLiteralExpression @int:
return @int.Value switch
{
// Bicep permits long values, but ARM's parser only permits int jtoken expressions.
// We can work around this by using the `json()` function to represent non-integer numerics.
> int.MaxValue or < int.MinValue => CreateFunction("json", new JTokenExpression(@int.Value.ToString(CultureInfo.InvariantCulture))),
_ => new JTokenExpression(@int.Value),
};
case StringLiteralExpression @string:
return new JTokenExpression(@string.Value);
case NullLiteralExpression _:
return CreateFunction("null");
case InterpolatedStringExpression @string:
return ConvertString(@string);
case ObjectExpression @object:
return ConvertObject(@object);
case ArrayExpression array:
return ConvertArray(array);
case UnaryExpression unary:
return ConvertUnary(unary);
case BinaryExpression binary:
return ConvertBinary(binary);
case TernaryExpression ternary:
return CreateFunction(
"if",
ConvertExpression(ternary.Condition),
ConvertExpression(ternary.True),
ConvertExpression(ternary.False));
case FunctionCallExpression function:
return CreateFunction(
function.Name,
function.Parameters.Select(ConvertExpression));
case UserDefinedFunctionCallExpression function:
return CreateFunction(
$"{EmitConstants.UserDefinedFunctionsNamespace}.{function.Symbol.Name}",
function.Parameters.Select(ConvertExpression));
case SynthesizedUserDefinedFunctionCallExpression function:
return CreateFunction(
$"{function.Namespace}.{function.Name}",
function.Parameters.Select(ConvertExpression));
case ImportedUserDefinedFunctionCallExpression importedFunction:
{
var (namespaceName, functionName) = GetFunctionName(context.SemanticModel.ImportClosureInfo.ImportedSymbolNames[importedFunction.Symbol]);
return CreateFunction(
$"{namespaceName}.{functionName}",
importedFunction.Parameters.Select(ConvertExpression));
}
case WildcardImportInstanceFunctionCallExpression importedFunction:
{
var (namespaceName, functionName) = GetFunctionName(
context.SemanticModel.ImportClosureInfo.WildcardImportPropertyNames[new(importedFunction.ImportSymbol, importedFunction.MethodName)]);
return CreateFunction(
$"{namespaceName}.{functionName}",
importedFunction.Parameters.Select(ConvertExpression));
}
case ResourceFunctionCallExpression listFunction when listFunction.Name.StartsWithOrdinalInsensitively(LanguageConstants.ListFunctionPrefix):
{
var resource = listFunction.Resource.Metadata;
var functionTargetExpression =
context.Settings.EnableSymbolicNames && resource is DeclaredResourceMetadata declared
? GenerateSymbolicReference(declared, listFunction.Resource.IndexContext)
: ConvertExpression(new PropertyAccessExpression(
listFunction.Resource.SourceSyntax,
listFunction.Resource,
"id",
AccessExpressionFlags.None));
var apiVersion = resource.TypeReference.ApiVersion ?? throw new InvalidOperationException($"Expected resource type {resource.TypeReference.FormatName()} to contain version");
var apiVersionExpression = new JTokenExpression(apiVersion);
var listArgs = listFunction.Parameters.Length switch
{
0 => new LanguageExpression[] { functionTargetExpression, apiVersionExpression, },
_ => new LanguageExpression[] { functionTargetExpression, }.Concat(listFunction.Parameters.Select(ConvertExpression)),
};
return CreateFunction(listFunction.Name, listArgs);
}
case PropertyAccessExpression exp:
return ConvertPropertyAccessExpression(exp);
case ModuleOutputPropertyAccessExpression exp:
return ConvertModuleOutputPropertyAccessExpression(exp);
case AccessExpression exp:
return ConvertAccessExpression(exp);
case ResourceReferenceExpression exp:
return GetReferenceExpression(exp.Metadata, exp.IndexContext, true);
case ModuleReferenceExpression exp:
return GetModuleReferenceExpression(exp.Module, exp.IndexContext, false);
case VariableReferenceExpression exp:
return CreateFunction("variables", new JTokenExpression(exp.Variable.Name));
case SynthesizedVariableReferenceExpression exp:
return CreateFunction("variables", new JTokenExpression(exp.Name));
case ImportedVariableReferenceExpression exp:
return CreateFunction("variables", new JTokenExpression(context.SemanticModel.ImportClosureInfo.ImportedSymbolNames[exp.Variable]));
case WildcardImportVariablePropertyReferenceExpression exp:
return CreateFunction("variables",
new JTokenExpression(context.SemanticModel.ImportClosureInfo.WildcardImportPropertyNames[new(exp.ImportSymbol, exp.PropertyName)]));
case ParametersReferenceExpression exp:
return CreateFunction("parameters", new JTokenExpression(exp.Parameter.Name));
case ParametersAssignmentReferenceExpression exp:
return CreateFunction("parameters", new JTokenExpression(exp.Parameter.Name));
case ExtensionReferenceExpression exp:
return CreateFunction("extensions", new JTokenExpression(exp.ExtensionNamespace.Name));
case LambdaExpression exp:
var variableNames = exp.Parameters.Select(x => new JTokenExpression(x));
var body = ConvertExpression(exp.Body);
return CreateFunction(
"lambda",
variableNames.Concat(body));
case LambdaVariableReferenceExpression exp:
if (exp.IsFunctionLambda)
{
return CreateFunction("parameters", new JTokenExpression(exp.Variable.Name));
}
return CreateFunction("lambdaVariables", new JTokenExpression(exp.Variable.Name));
case CopyIndexExpression exp:
return exp.Name is null
? CreateFunction("copyIndex")
: CreateFunction("copyIndex", new JTokenExpression(exp.Name));
default:
throw new NotImplementedException($"Cannot emit unexpected expression of type {expression.GetType().Name}");
}
}