Referencing value assigned to variable in statement builder within another statement

Given a statement that does something like:
var expr = new ExpressionBuilder();
expr.AppendVerbatim("var propertyValue = (");
expr.AppendType(field.Type);
expr.AppendVerbatim(")valueType?.GetValue(");
//And so on
var expr = new ExpressionBuilder();
expr.AppendVerbatim("var propertyValue = (");
expr.AppendType(field.Type);
expr.AppendVerbatim(")valueType?.GetValue(");
//And so on
I would like to use the value assigned to propertyValue elsewhere, but of course, I cannot use AppendExpression with it since the context isn't aware it's been assigned. How might I go about referencing this variable value as an expression? Thanks!
10 Replies
Petr Onderka
Petr Onderka17mo ago
I think that ExpressionFactory.Parse("propertyValue") should work for you. (It's effectively the same as using ExpressionBuilder and then calling ToExpression() on it.)
Whit
WhitOP17mo ago
Well, let me put it another way. Say I use the statement builder with the following:
var sampleBuilder = new StatementBuilder();
sampleBuilder.AppendVerbatim("var abc = ");
sampleBuilder.AppendLiteral(123);
sampleBuilder.AppendVerbatim(";");
meta.InsertStatement(sampleBuilder.ToStatement());
var abc = ExpressionFactory.Parse("abc").Value;
var sampleBuilder = new StatementBuilder();
sampleBuilder.AppendVerbatim("var abc = ");
sampleBuilder.AppendLiteral(123);
sampleBuilder.AppendVerbatim(";");
meta.InsertStatement(sampleBuilder.ToStatement());
var abc = ExpressionFactory.Parse("abc").Value;
When I insert the statement via meta.InsertStatement, this doesn't give me a variable I can reference in other parts of the template. The use of ExpressionFactory.Parse on the next line does give me abc to use in the template, but this suddenly can't be used in a compile-time loop as it outputs the following:
var abc = 123;
var abc = abc;

var abc = 123;
var abc_1 = abc;

var abc = 123;
var abc_2 = abc;
var abc = 123;
var abc = abc;

var abc = 123;
var abc_1 = abc;

var abc = 123;
var abc_2 = abc;
I suppose I could do something like:
//Out of the loop
var ii = meta.CompileTime(0);

//In the loop
var sampleBuilder = new StatementBuilder();
sampleBuilder.AppendVerbatim($"var abc{ii} = ");
sampleBuilder.AppendLiteral(123);
sampleBuilder.AppendVerbatim(";");
meta.InsertStatement(sampleBuilder.ToStatement());
var abc = ExpressionFactory.Parse($"abc{ii}").Value;
//Out of the loop
var ii = meta.CompileTime(0);

//In the loop
var sampleBuilder = new StatementBuilder();
sampleBuilder.AppendVerbatim($"var abc{ii} = ");
sampleBuilder.AppendLiteral(123);
sampleBuilder.AppendVerbatim(";");
meta.InsertStatement(sampleBuilder.ToStatement());
var abc = ExpressionFactory.Parse($"abc{ii}").Value;
And then this yields (outside of the apparently bug reported at https://github.com/postsharp/Metalama/issues/202):
var abc0 = 123;
var abc = abc0;

var abc1 = 123;
var abc_1 = abc1;

var abc2 = 123;
var abc_2 = abc2;
var abc0 = 123;
var abc = abc0;

var abc1 = 123;
var abc_1 = abc1;

var abc2 = 123;
var abc_2 = abc2;
Again, since the first variable is more than sufficient for my purposes but is simply inaccessible since it's created by string, might you consider updating the StatementBuilder to include an out variable or the like that I can use to reference the variable on the class instead of this hacky approach resulting in two lines in the output for each assignment (especially since the ExpressionFactory.Parse().Value approach itself yields a unique variable name each time? Thanks!
GitHub
Issues · postsharp/Metalama
Metalama is a Roslyn-based meta-programming framework. Use this repo to report bugs or ask questions. - Issues · postsharp/Metalama
Gael Fraiteur
Gael Fraiteur17mo ago
OK I got it. Didn't read your first request properly it seems However generally we want to keep StatementBuilder and ExpressionBuilder as minimalistic as possible. So you want an API that defines a local variable and takes care of the unique naming?
Petr Onderka
Petr Onderka17mo ago
If you want to reference the variable you declared, you can do it by declaring var abc = ExpressionFactory.Parse("abc"); and then using abc.Value where you used just abc previously. Alternatively, I wonder if using Builder just for the expression and letting Metalama manage the variable would work better. Let me check. Yeah, I think that would work better for you. For example, this code:
foreach (var i in meta.CompileTime(Enumerable.Range(0, 3)))
{
var sampleBuilder = new ExpressionBuilder();
sampleBuilder.AppendLiteral(123);
var abc = sampleBuilder.ToValue();
Console.WriteLine(abc);
}
foreach (var i in meta.CompileTime(Enumerable.Range(0, 3)))
{
var sampleBuilder = new ExpressionBuilder();
sampleBuilder.AppendLiteral(123);
var abc = sampleBuilder.ToValue();
Console.WriteLine(abc);
}
generates:
var abc = 123;
Console.WriteLine(abc);
var abc_1 = 123;
Console.WriteLine(abc_1);
var abc_2 = 123;
Console.WriteLine(abc_2);
var abc = 123;
Console.WriteLine(abc);
var abc_1 = 123;
Console.WriteLine(abc_1);
var abc_2 = 123;
Console.WriteLine(abc_2);
Whit
WhitOP17mo ago
I'll see if I can't use that - I suspect I'm going to run into issues along the lines of https://github.com/postsharp/Metalama/issues/202 since this is within several if and foreach loops both run-time and compile-time
GitHub
Bug: Metalama not incrementing in compile-time loop as expected · I...
I'm trying to solve a variable reference issue. Put simply, I have a loop inside a template that enumerates several compile-time values to write several run-time statements. As these are writte...
Petr Onderka
Petr Onderka17mo ago
I think you shouldn't, since you wouldn't be modifying any compile-time variables.
Whit
WhitOP17mo ago
Yeah, unfortunately I think my toy sample there doesn't quite work with what I actually have in place - can't see how to fit that in there. I've got a foreach going through a compile-time dictionary, a foreach inside that going through the values of that kv pair, then a null check on each of those values (compile-time), then a run-time null check (per issue #202 up there) and then inside all that I've got one more equality check and the statement builder. Right now I'm incrementing the meta.CompileTime after the run-time null check but at the bottom of the inner foreach. If not for all my loops already there, I think that'd be a clever fix To your question @the.metalama.guy Yes, that'd be great - an API that lets me simply provide the right side of a statement after the "=" and places its own unique variable name in there so I might trivially reference it anywhere else. Like what I get with expression builder - don't much care what the auto-generated variable name is, so long as it's unique and I can trivially reference it in the rest of the template
Gael Fraiteur
Gael Fraiteur17mo ago
I think it's rather going to be a method like: IExpression DefineLocalVariable( string nameHint, IType type, IExpression? assignedValue ); Please file a feature request on GitHub because we don't track tickets here -- just for ephemeral discussions
Whit
WhitOP17mo ago
Absolutely - I've been thinking about what I'd like it to do, but I'll file a ticket about it in a few Filed under #205
Want results from more Discord servers?
Add your server