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.
Related
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" + " ; }))(); ");
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'
With string interpolation, how do you handle variables piped into a command that contain spaces in them? For example, if you have a variable that has spaces in it (like a UNC path), how do you handle that?
This code works when no spaces are present in the "filePath" variable (i.e.; \ServerName\testfile.txt):
Ex: System.Diagnostics.Process.Start("net.exe", $"use X: \\{filePath} {pwd /USER:{usr}").WaitForExit();
As soon as you encounter a path that has spaces in it, however, the command above no longer works, because it's unable to find the path. Normally, I would apply quotes around a path containing spaces, to counter this (in other languages like PowerShell). How do you do something similar with C# interpolation.
C# 6.0+:
System.Diagnostics.Process.Start("net.exe", #$"use X: \\Servername\share {pwd} /USER:{usr}").WaitForExit();
C# < 6.0:
System.Diagnostics.Process.Start("net.exe", #"use X: \\Servername\share " + pwd + " /USER: " + usr).WaitForExit();
use the $
void Main()
{
string pwd = "test";
var myVar = $"This is a {pwd}";
var folder = "MyFolder";
var myVarWithPaths = $"C:\\{folder}";
Console.WriteLine(myVar);
Console.WriteLine(myVarWithPaths);
}
Output
This is a test
C:\MyFolder
C# 6.0 introduced string interpolation, which is used by prefixing a quoted string of text with the $ character.
e.g.
var i = 0;
var s = $"i = {i}";
// output: i = 0
You can also embed multiple interpolated strings, as well as conditions.
var i = 0;
var s = $"i is: {(i == 1 ? $"i is {1}" : "i is not 1")}";
This can be combined with string literals that are prefixed with #.
var i = 1;
var s = #$"c:\{i}\test";
Basically, you can write almost any normal expression statement in a interpolated string, such as calling methods:
var s = $"i is {GetValueOfI() - 100}";
For types that are not a System.String, the implementation of that types ToString() method will be used for the resulting value.
See: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated
I'm relatively new to C# and trying to deserialize a string from JSON for this value; ["format"]["tags"]["ENCODER"]
// Manually building the string works fine
string test = (dict["format"]["tags"]["ENCODER"]);
string found_value = "";
const char quote = '\u0022';
string encoder = "[" + quote + "format" + quote + "][" + quote + "tags" + quote + "][" + quote + "ENCODER" + quote + "]";
// Just as a test
encoder = encoder.Replace("\u005c\u0022", "\u0022");
// This Fails
found_value = (dict[encoder]);
It throws an exception of type 'System.Collections.Generic.KeyNotFoundException' occurred in mscorlib.dll but was not handled in user code
Additional information: The given key was not present in the dictionary.
So I'm sure it's the way I'm passing the encoder string. Probably something really simple but I've spent hours trying various things and become blind to it now.
Thanks in advance
Basically, C# isn't a scripting language, but is a compiled language. This means that there is no equivalent of a javascript eval() function, and strings don't perform the same as the equivalent code string. So the second lookup, you're trying to do this:
dict["[\"format\"][\"tags\"][\"eval\"]")
And it is rightly complaining that your first dictionary there doesn't have a key of the name
"[\"format\"][\"tags\"][\"eval\"]"
Why are you trying to do this in the second way, when the first one works?
I'm trying to get a Text description from a website and used this code
HttpResponseMessage response1 = await httpClient.GetAsync(url);
response1.EnsureSuccessStatusCode();
string srcCode = await response1.Content.ReadAsStringAsync();
string desc = "";
rem = #"id=""full_notes"">";
if (srcCode.IndexOf(rem) != -1)
{
desc = srcCode.Remove(0, srcCode.IndexOf(rem) + rem.Length);
rem = #"less</span>";
desc = desc.Remove(desc.IndexOf(rem));
}
else
{
rem = #"<span>Description:</span>";
desc = srcCode.Remove(0, srcCode.IndexOf(rem) + rem.Length+15);
rem = "</div>";
desc = desc.Remove(desc.IndexOf(rem));
}
string rep = #"""";
desc.Replace(""",rep);
on.Description = desc;
all goes well and I show it in a textblock but the problem comes when the text I get has quotes "" then the textblock would only show " instead of the "
anyone knows how to fix this?
Note: I tried to use replace as shown in the code it didn't work
You're calling Replace but not doing anything with the result - strings are immutable in C#, so the Replace method doesn't change the contents of the existing string. As the documentation states:
This method does not modify the value of the current instance. Instead, it returns a new string in which all occurrences of oldValue are replaced by newValue.
So you'd need:
desc = desc.Replace(""", "\"");
Fundamentally though, I think you'd be better off using an HTML parser (e.g. HTML Agility Pack). That should be able to perform this sort of entity replacement for you.