How to use SyntaxFactory for roslyn - c#

I am trying to generate code using SyntaxFactory but stuck in converting one area Please see the code below I left comment where I am finding it difficult and used string manipulation.
The idea is there will be two variable removeExpression and getExpression which are of type ExpressionSyntax and finally both will be used to become the body of a anonymous function.
ExpressionSyntax removeExpression = SyntaxGenerator.MethodInvokeExpression(
valueExpression,
"RemoveAt",
BinaryExpression(
SyntaxKind.SubtractExpression,
SyntaxGenerator.PropertyAccessExpression(valueExpression, nameof(Enumerable.Count)),
atExpression
)
);
ExpressionSyntax getExpression = SyntaxGenerator.MethodInvokeExpression(valueExpression, nameof(Enumerable.First));
// Help me to convert the below line using SyntaxFactory.
IdentifierName("((Func<dynamic>)(() => { var res = " + getExpression.ToString() + ";" + removeExpression.ToString() + "; return res" + " ; }))(); ");

Related

How to add emoji to a string then add to .ToList

I'm currently trying to create a Poll command for my Discord BOT. I would like to edit the string answer and add an Emoji to the start of that answer based on the number the answer is given.
E.G the user will type ?poll "questionhere" "answer1" "answer2" - i would like to take "answer1" and change to ":one: answer1" and so on but i'm not sure how to go about doing that.
The second thing i would like some advise of if possible is how i am currently managing how many reactions get added to the message, there has got to be a better way of doing what i am doing but i am still fairly new to this and learning so any advise or recommendation on how to better structure the below would be appreciated .
//Take the string answer and split into separate strings based on text inside ""
var result = answer.Split('"').Where((x, i) => i % 2 == 1).ToList();
if (result.Count() == 2)
{
List<List<string>> listChunks = StaticObjects.SplitIntoChunks<string>(result, 50);
for (int i = 0; i < listChunks.Count; i++)
{
listChunks = listChunks.OrderBy(x => x).ToList();
string description = String.Join(Environment.NewLine, listChunks[i]);
EmbedBuilder eb = new EmbedBuilder()
{
Title = $#":bar_chart:**{question.ToUpper()}**",
Description = description,
Color = new Color(0, 0, 127),
};
var message = await ReplyAsync(embed: eb.Build());
var YourEmoji1 = new Emoji("1\u20e3");
var YourEmoji2 = new Emoji("2\u20e3");
await message.AddReactionAsync(YourEmoji1);
await message.AddReactionAsync(YourEmoji2);
currently then continues with if (result.Count() == 3)
Update
I've found a way to add the Emoji before the string however now i don't know how to pass it back to the original result
var one = "1\u20e3" + result.ElementAt(0);
Update 2
I've now managed to get this working :)
added this
if (result.Count() == 2)
{
var one = "1\u20e3" + " " + "-" + " " + result.ElementAt(0);
var two = "2\u20e3" + " " + "-" + " " + result.ElementAt(1);
and changed result to one, two
string description = String.Join(Environment.NewLine, one, two);
Update 3
I've discovered you can change
var YourEmoji1 = new Emoji("1\u20e3");
var YourEmoji2 = new Emoji("2\u20e3");
await message.AddReactionAsync(YourEmoji1);
await message.AddReactionAsync(YourEmoji2);
to
await message.AddReactionAsync(new Emoji("1\u20e3"));
await message.AddReactionAsync(new Emoji("2\u20e3"));
I'm now just left with the problem of structuring this in a much more elegant way so the code isn't ridiculously long as i want to allow up to 10 answers
Since you've found your solution and now want to just clean up the code, I just have a suggestion for readability:
You don't need to use character codes when you declare your emojis (new Emoji("1\u20e3")). Instead, you can use the Discord emoji names, as long as you include the :s.
For example: new Emoji(":one:"), new Emoji(":two:") etc
Similarly, the following can be cleaned up
var one = "1\u20e3" + " " + "-" + " " + result.ElementAt(0);
To this:
var one = ":one: - " + result.ElementAt(0);
And so on.
This will not make your code work any better, however it will make it more readable.

C# Python input method returns an error

I've just started to learn IronPython and I tried the code below which returned IronPython.Runtime.UnboundNameException: 'name 'hello' is not defined'.
Code:
var py = Python.CreateEngine();
var scope = py.CreateScope();
py.Execute(#"word = input('Input string\n')", scope);
var input = scope.GetVariable("word");
py.Execute("print " + input);
Console run ok, then it asked me to Input string, and I typed into "hello". And then it fired off the above error message. Then I tried this one just to see if it does without input method:
py.Execute(#"x = 2 + 3", scope);
py.Execute("print 'result'," + scope.GetVariable("x"));
So that one was ok.
Can someone please explain why can I not retrieve a variable from "input" method? and why is it an UnboundNameException?
Many thanks!
Having never worked with ironpython the answer lies within your own code.
your code:
py.Execute(#"word = input('Input string\n')", scope); (I type in dog)
var input = scope.GetVariable("word");
py.Execute("print " + input);
results in that last line saying py.Execute("print dog") ... but there is no dog varaible.
yet here:
py.Execute("print 'result'," + scope.GetVariable("x"));
You know to encapsulate text in quotes..
Id speculate that
py.Execute("print " + input);
Should read
py.Execute("print '" + input + "'");
Which results in print 'dog'

Crystal report textobject and fields

i am running the following code
foreach (ReportObject obj in oSectionObjects)
{
if (obj.Kind == CrystalDecisions.Shared.ReportObjectKind.TextObject)
{
// do stuff
}
}
but i have a problem. i do have multiple text that do contain text AND fields in them.
But crystal return me the field being TextObject which is technically true.
How do i know i ONLY have text in the TextObject and not anything else (aka fields, parameters, formulas) ?
As far as I know the fields in a text box will be recognized by the text pattern. Try to search the text of the text object for {1#xxxxx} where xxxxx is the field name. "{1#" shows the type of the field: 1 is for a database , 2 is for formula, 3 is for parameter. You may try also for {#xxxxx} *(without numeric field identifier)
I searched alot around and found working solution for RAS report but nothing for crystal. Anyhow if someone end up here looking for an answer here's the work around.
Whenever you have to concatenate multiple fields on the report do NOT use TextObject. Instead use a Formula. The formula fields wont bet part of the ReportObjects but instead part of the ReportDocument.DataDefinition.FormulaFields with Kind being CrystalDecisions.Shared.FieldKind.FormulaField and you will want to check the ValueType so it is CrystalDecisions.Shared.FieldValueType.StringField.
then you can manipulate them.
I did need that for translation of report live so here's a parsing method for formulas :
try
{
var sFormula = formula.Text;
string pattern = "\"[\\w ]*\"";
Regex r = new Regex(pattern);
MatchCollection mc = r.Matches(sFormula);
foreach (Match m in mc)
{
var sValue =m.Value;
var sParsedValue = sValue.Substring(1, sValue.Length - 2);
if (sParsedValue.StartsWith("s"))
{
var stest = "\"" + CApplicationData.TranslateStringValue(sParsedValue) + "\"";
sFormula = sFormula.Replace(sValue, stest);
}
}
formula.Text = sFormula;
}
catch{}
this above you will notice i use 's' as a key to know it might be a value to be translated so it's not mandatory. using the above on this formula with Spanish language :
"sPage" + " " + totext(PageNumber) + " " + "sOf" + " " + totext(TotalPageCount)
will modify the formula to :
"Página" + " " + totext(PageNumber) + " " + "de" + " " + totext(TotalPageCount)
giving output of :
Página 1 de 4

String escaping issue with Roslyn statement creation

I've the following code, which builds up a Roslyn statement which calls Roslyn code inside, but I've a problem with string escaping.
Here is the code:
var parseStatementArgument = "var statement = Syntax.ParseStatement(\\\"Console.WriteLine (\\\"Hello {0}\\\", parameter1);\\\");";
var st = Syntax.InvocationExpression(
Syntax.MemberAccessExpression(SyntaxKind.MemberAccessExpression, Syntax.IdentifierName("Syntax"), Syntax.IdentifierName("ParseStatement")))
.AddArgumentListArguments(
Syntax.Argument(Syntax.LiteralExpression(
SyntaxKind.StringLiteralExpression,
Syntax.Literal(
text: "\"" + parseStatementArgument + "\"",
value: parseStatementArgument)
)));
var variableDeclarator = Syntax.VariableDeclarator(Syntax.Identifier("statement"))
.WithInitializer(Syntax.EqualsValueClause(st));
var varStatement = Syntax.VariableDeclaration(Syntax.IdentifierName("var"), Syntax.SeparatedList(variableDeclarator));
var varStatementText = varStatement.Format().GetFormattedRoot().GetFullText() + ";";
var scriptEngine = new ScriptEngine(
new [] {
MetadataReference.Create("Roslyn.Compilers"),
MetadataReference.Create("Roslyn.Compilers.CSharp"),
MetadataReference.Create("Roslyn.Services"),
MetadataReference.Create("Roslyn.Services.CSharp")
},
new [] {
"System",
"Roslyn.Compilers.CSharp",
"Roslyn.Scripting",
"Roslyn.Scripting.CSharp",
"Roslyn.Services"
});
var session = Session.Create();
scriptEngine.Execute(varStatementText, session);
scriptEngine.Execute("Console.WriteLine (statement.Format().GetFormattedRoot().GetFullText());", session);
The problem is that the "statement" printed to the console windows via the script engine execution will miss the backslashed around the "Hello {0}" string. If I add double escaping (additional \ into the parameter, Roslyn will raise compile errors about missing commas.
How may I update this code to get a syntactically correct version of what I want into the statement variable?
How about switching to using verbatim string levels, and just add another level of escaping as you add the node.
Something like:
var parseStatementArgument = #"var statement = Syntax.ParseStatement(#""Console.WriteLine (""""Hello {0}"""", parameter1);"");";
var st = Syntax.InvocationExpression(
Syntax.MemberAccessExpression(SyntaxKind.MemberAccessExpression, Syntax.IdentifierName("Syntax"), Syntax.IdentifierName("ParseStatement")))
.AddArgumentListArguments(
Syntax.Argument(Syntax.LiteralExpression(
SyntaxKind.StringLiteralExpression,
Syntax.Literal(
text: "#\"" + parseStatementArgument.Replace("\"", "\"\"") + "\"",
value: parseStatementArgument)
)));
Based on Kevin's tip on how to replace string for literals I played around and found this as a solution that works, but it raised another problem.
The solution:
var parseStatementArgument = "var statement = Syntax.ParseStatement(\\\"Console.WriteLine (\\\\\\\"Hello {0}\\\\\\\", parameter1);\\\");";
var st = Syntax.InvocationExpression(
Syntax.MemberAccessExpression(SyntaxKind.MemberAccessExpression, Syntax.IdentifierName("Syntax"), Syntax.IdentifierName("ParseStatement")))
.AddArgumentListArguments(
Syntax.Argument(Syntax.LiteralExpression(
SyntaxKind.StringLiteralExpression,
Syntax.Literal(
text: "\"" + parseStatementArgument + "\"",
value: parseStatementArgument.Replace ("\\\\\\", "\\"))
)));
Now it correctly outputs a code snippet which is syntactically correct and compiles well.
The problem it raises is that I had to modify the source string and not the derived string to get the correct result. When rewriting code or generating code with Roslyn it can not be a requirement to double or triple escape string literals to make Roslyn able to deal with that correctly, maybe its a Roslyn issue, I hope that someone will shed some light on an elegant solution which works for all kind of strings.

Ternary operator; What is wrong with this syntax?

I'm trying to create a MailMessage, and I'm getting the following error...
Cannot implicitly convert type 'string' to 'bool'
This is my init statement:
MailMessage msg = new MailMessage("DoNotReply#optoma.com",
myTbl.Rows[i]["Requester"].ToString().Trim(),
subject,
"Dear " + myTbl.Rows[i]["Ship_Attention"].ToString() + ",<br/><br/>" +
body + "<br/>Your ISO ID is " + myTbl.Rows[i]["ISO_ID"].ToString() +
(Convert.ToInt32(myTbl.Rows[i]["EmailType"]) == 1) ?
("<br/>Tracking Number: " + myTbl.Rows[i]["Tracking_No"].ToString()) :
("") + "<br/><br/>Please examine the loaned items for this transaction:<br/><br/>" +
sw.ToString());
I'm trying to add to the string at runtime based on a boolean expression. Why can't I do this? Am I not doing it right?
string + (true | false) ? "somestring" : "anotherstring" + string
the ? : operator has very low precedence. Put it in parenthesis and I think you'll resolve your issue.
((true|false)?"somestring":"anotherstring")
When you have string + (bool)?"somestring":"anotherstring" + string the + gets evaluated before the ?, so you need parentheses:
string + ((bool)?"somestring":"anotherstring") + string
just cleaning it up a wee bit.... and you won't run into operator precedence problems so much
void SendMessage(DataRow row, string subject, string body, string sw)
{
var to = row["Requester"].ToString().Trim();
var isoId = row["ISO_ID"].ToString();
var attention = row["Ship_Attention"].ToString();
var emailType = Convert.ToInt32(row["EmailType"]);
var message = (emailType == 1) ? ("<br/>Tracking Number: " + row["Tracking_No"]) : ("");
MailMessage msg = new MailMessage("DoNotReply#optoma.com",
to,
subject,
string.Format("Dear {0},<br/><br/>{1}<br/>Your ISO ID is {2}{3}<br/><br/>Please examine the loaned items for this transaction:<br/><br/>{4}",
attention, body, isoId, message, sw));
}
The precedence isn't what you expect -- + is being evaluated first. Your code should be in the form:
string + (true|false ? "somestring" : "anotherstring") + string
For your specific example:
MailMessage msg = new MailMessage("DoNotReply#optoma.com", myTbl.Rows[i]["Requester"].ToString().Trim(),
subject, "Dear " + myTbl.Rows[i]["Ship_Attention"].ToString() + ",<br/><br/>" +
body + "<br/>Your ISO ID is " + myTbl.Rows[i]["ISO_ID"].ToString() + (Convert.ToInt32(myTbl.Rows[i]["EmailType"]) == 1 ? ("<br/>Tracking Number: " + myTbl.Rows[i]["Tracking_No"].ToString()) : ("")) + "<br/><br/>Please examine the loaned items for this transaction:<br/><br/>" +
sw.ToString());
Note that this is a very long expression and should probably be broken down into several statements to make it more readable and maintainable.
The additive oprator (+) has higher priority than conditional (?:) as indicated here: http://msdn.microsoft.com/en-us/library/aa691323%28v=vs.71%29.aspx.
Therefore you need to put parenthesis around the whole condition:
string + ((true|false)?"somestring":"anotherstring") + string
I suggest you to divide your code into more lines, introduce some temporary variables and use string.format() to make it look clener. Finding errors in clean code is much easier.

Categories

Resources