I have a program that generates C# from bits of C# stored in an XML file. If I have a snippet like:
foo {bar}
I need to transform that into an interpolated string, like this:
$#"foo {bar}"
The problem is that, if I have quotes outside a placeholder, e.g.:
"foo" {bar}
I need to double those:
$#"""foo"" {bar}"
but ignore quotes inside placeholders:
foo {"bar"}
should produce:
$#"foo {"bar"}"
Also, need to look out for doubled braces:
foo {{"bar"}}
should produce:
$#"foo {{""bar""}}"
And perhaps the trickiest of all, if the placeholder is preceded and/or followed by an even number of braces:
foo {{{"bar"}}}
should produce:
$#"foo {{{"bar"}}}"
In short, if there's a placeholder then ignore everything inside. For the rest of the text, double quotes.
Can this be accomplished using regular expressions? If not, what alternatives do I have?
You will need at least 2 steps:
Add quotes inside the expression:
"(?=[^}]*(?:}})*[^}]*$)|(?<=^[^{]*(?:{{)*)" =>
replace with ""
See demo
Enclose in $#"..." with string.Format("$#\"{0}\"", str);
Here is an IDEONE demo
var s = "\"foo\" {bar}";
var rx = new Regex(#"(?<!(?<!{){[^{}]*)""(?![^{}]*}(?!}))");
Console.WriteLine(string.Format("$#\"{0}\"",rx.Replace(s,"\"\"")));
And another demo here
This cannot be done with regular expressions. Knowing when a placeholder starts is easy, knowing when it ends is the hard part, since a placeholder can hold almost any C# expression, so you have to keep track of blocks ({}) and literals (strings, chars, comments) because any brace in a literal is not significant.
This is the code I came up with:
enum ParsingMode {
Text,
Code,
InterpolatedString,
InterpolatedVerbatimString,
String,
VerbatimString,
Char,
MultilineComment
}
public static string EscapeValueTemplate(string valueTemplate) {
if (valueTemplate == null) throw new ArgumentNullException(nameof(valueTemplate));
var quoteIndexes = new List<int>();
var modeStack = new Stack<ParsingMode>();
modeStack.Push(ParsingMode.Text);
Func<ParsingMode> currentMode = () => modeStack.Peek();
for (int i = 0; i < valueTemplate.Length; i++) {
char c = valueTemplate[i];
Func<char?> nextChar = () =>
i + 1 < valueTemplate.Length ? valueTemplate[i + 1]
: default(char?);
switch (currentMode()) {
case ParsingMode.Code:
switch (c) {
case '{':
modeStack.Push(ParsingMode.Code);
break;
case '}':
modeStack.Pop();
break;
case '\'':
modeStack.Push(ParsingMode.Char);
break;
case '"':
ParsingMode stringMode = ParsingMode.String;
switch (valueTemplate[i - 1]) {
case '#':
if (i - 2 >= 0 && valueTemplate[i - 2] == '$') {
stringMode = ParsingMode.InterpolatedVerbatimString;
} else {
stringMode = ParsingMode.VerbatimString;
}
break;
case '$':
stringMode = ParsingMode.InterpolatedString;
break;
}
modeStack.Push(stringMode);
break;
case '/':
if (nextChar() == '*') {
modeStack.Push(ParsingMode.MultilineComment);
i++;
}
break;
}
break;
case ParsingMode.Text:
case ParsingMode.InterpolatedString:
case ParsingMode.InterpolatedVerbatimString:
switch (c) {
case '{':
if (nextChar() == '{') {
i++;
} else {
modeStack.Push(ParsingMode.Code);
}
break;
case '"':
switch (currentMode()) {
case ParsingMode.Text:
quoteIndexes.Add(i);
break;
case ParsingMode.InterpolatedString:
modeStack.Pop();
break;
case ParsingMode.InterpolatedVerbatimString:
if (nextChar() == '"') {
i++;
} else {
modeStack.Pop();
}
break;
}
break;
case '\\':
if (currentMode() == ParsingMode.InterpolatedString) {
i++;
}
break;
}
break;
case ParsingMode.String:
switch (c) {
case '\\':
i++;
break;
case '"':
modeStack.Pop();
break;
}
break;
case ParsingMode.VerbatimString:
if (c == '"') {
if (nextChar() == '"') {
i++;
} else {
modeStack.Pop();
}
}
break;
case ParsingMode.Char:
switch (c) {
case '\\':
i++;
break;
case '\'':
modeStack.Pop();
break;
}
break;
case ParsingMode.MultilineComment:
if (c == '*') {
if (nextChar() == '/') {
modeStack.Pop();
i++;
}
}
break;
}
}
var sb = new StringBuilder(valueTemplate, valueTemplate.Length + quoteIndexes.Count);
for (int i = 0; i < quoteIndexes.Count; i++) {
sb.Insert(quoteIndexes[i] + i, '"');
}
return sb.ToString();
}
Related
I apologize for asking this question in this manner, but I evidently don't have enough "Reputation" to post a question as a comment in the originating thread.
Someone here made a nice little class to properly indent a JSON string so it's more human-friendly. It works great, except that I'd like to modify it so that empty arrays are represented by "[]" rather than having a bunch of whitespace between the characters (newline, likely several indent characters, etc.). It seemed like such a simple plan.
The original code looks like this:
private const string INDENT_STRING = " ";
public static string FormatJson(string str)
{
var indent = 0;
var quoted = false;
var sb = new StringBuilder();
for (var i = 0; i < str.Length; i++)
{
var ch = str[i];
switch (ch)
{
case '{':
case '[':
sb.Append(ch);
if (!quoted)
{
sb.AppendLine();
Enumerable.Range(0, ++indent).ForEach(item => sb.Append(INDENT_STRING));
}
break;
case '}':
case ']':
if (!quoted)
{
sb.AppendLine();
Enumerable.Range(0, --indent).ForEach(item => sb.Append(INDENT_STRING));
}
sb.Append(ch);
break;
case '"':
sb.Append(ch);
bool escaped = false;
var index = i;
while (index > 0 && str[--index] == '\\')
escaped = !escaped;
if (!escaped)
quoted = !quoted;
break;
case ',':
sb.Append(ch);
if (!quoted)
{
sb.AppendLine();
Enumerable.Range(0, indent).ForEach(item => sb.Append(INDENT_STRING));
}
break;
case ':':
sb.Append(ch);
if (!quoted)
sb.Append(" ");
break;
default:
sb.Append(ch);
break;
}
}
return sb.ToString();
}
I tried modifying it so it had this in it:
case '[':
sb.Append(ch);
if (!quoted)
{
if (str[i + 1] != ']')
{
sb.AppendLine();
Enumerable.Range(0, ++indent).ForEach(item => sb.Append(INDENT_STRING));
}
}
break;
It blows up on me, complaining that I'm referencing an index that's too large (actually, it complains about something that has a number of possibilities, 'ArgumentOutOfRangeException'
being the most likely). I tried adding a tracker to see if i+1 > str.Length, but it still blows up. And the spot in the string it's blowing up at isn't anywhere near a [ or a ]. Indeed, ch is a '{' and str[i+1] is a ','.
Am I making any sense?
I thought about just taking the resulting string and using Regex to rip out any whitespace between [ and ], but that seemed inelegant.
Does anyone have a recommendation for how to modify this otherwise-excellent code to be the way I want? I tried, really I did...
I have used a different approach. When i encounter a square bracket i check where is the pairing square bracket, and verify it's not an empty string in between. The expression is this:
isEmptyArray = nextMatchingBracketIndex != -1 ? stringRemainder.Substring(1, nextMatchingBracketIndex - 1).Trim().Length == 0 : false;
The full working code is:
class JsonHelper
{
private const string INDENT_STRING = " ";
public static string FormatJson(string str)
{
var indent = 0;
var quoted = false;
var isEmptyArray = false;
var sb = new StringBuilder();
char ch = default(char);
for (var i = 0; i < str.Length; i++)
{
ch = str[i];
switch (ch)
{
case '{':
sb.Append(ch);
if (!quoted)
{
sb.AppendLine();
Enumerable.Range(0, ++indent).ForEach(item => sb.Append(INDENT_STRING));
}
break;
case '[':
sb.Append(ch);
var stringRemainder = str.Substring(i);
var nextMatchingBracketIndex = stringRemainder.IndexOf(']');
isEmptyArray = nextMatchingBracketIndex != -1 ? stringRemainder.Substring(1, nextMatchingBracketIndex - 1).Trim().Length == 0 : false;
if (!quoted && !isEmptyArray)
{
sb.AppendLine();
Enumerable.Range(0, ++indent).ForEach(item => sb.Append(INDENT_STRING));
}
break;
case '}':
if (!quoted)
{
sb.AppendLine();
Enumerable.Range(0, --indent).ForEach(item => sb.Append(INDENT_STRING));
}
sb.Append(ch);
break;
case ']':
if (!quoted && !isEmptyArray)
{
sb.AppendLine();
Enumerable.Range(0, --indent).ForEach(item => sb.Append(INDENT_STRING));
}
sb.Append(ch);
isEmptyArray = false;
break;
case '"':
sb.Append(ch);
bool escaped = false;
var index = i;
while (index > 0 && str[--index] == '\\')
escaped = !escaped;
if (!escaped)
quoted = !quoted;
break;
case ',':
sb.Append(ch);
if (!quoted)
{
sb.AppendLine();
Enumerable.Range(0, indent).ForEach(item => sb.Append(INDENT_STRING));
}
break;
case ':':
sb.Append(ch);
if (!quoted)
sb.Append(" ");
break;
default:
if (!isEmptyArray)
sb.Append(ch);
break;
}
}
return sb.ToString();
}
}
Here are the test that i verified to work as expected:
Empty types array:
"types":[]
Empty types array with characters in between:
"types":[ ]
Full types array with empty characters at the beginning
"types":[ "locality", "political"]
note: in 1,2,3 i refer to a substring in the original JSON file provided in your link:
{"status":"OK", "results":[ {"types":[ "locality", "political"], "formatted_address":"New York, NY, USA", "address_components":[ {"long_name":"New York", "short_name":"New York", "types":[ "locality", "political"]}, {"long_name":"New York", "short_name":"New York", "types":[ "administrative_area_level_2", "political"]}, {"long_name":"New York", "short_name":"NY", "types":[ "administrative_area_level_1", "political"]}, {"long_name":"United States", "short_name":"US", "types":[ "country", "political"]}], "geometry":{"location":{"lat":40.7143528, "lng":-74.0059731}, "location_type":"APPROXIMATE", "viewport":{"southwest":{"lat":40.5788964, "lng":-74.2620919}, "northeast":{"lat":40.8495342, "lng":-73.7498543}}, "bounds":{"southwest":{"lat":40.4773990, "lng":-74.2590900}, "northeast":{"lat":40.9175770, "lng":-73.7002720}}}}]}
I have tested the output i got in different tests with JSONLint and it was valid.
It's 1 a.m. my time and I am pretty sure there are some edge case i haven't covered. But this code should give you an alternative approach that works better than what you have right now.
could I turn this into a switch statement ?
if (donation_euro.Text.Trim().Equals(""))
{
donation_euro.Text = "00.00";
}
if (donation_lui.Text.Trim().Equals(""))
{
donation_lui.Text = "00.00";
}
if (donation.Text.Trim().Equals(""))
{
donation.Text = "00.00";
}
No, because your are not switching on a single variable, but multiple.
I suspect your motivation to do this, is to make the code more readable ? If this is the case, you could put the common logic of your three if's into a method, to reuse the code and better convey the intent.
Not Possible.as switch takes Expression and executes the matching Constant Case Label.
From MSDN :Switch-Case
Each case label specifies a constant value. The switch statement
transfers control to
the switch section whose case label matches
the value of the switch expression
Switch(Expression)
{
case constant1://statements
break;
case constant2://statements
break;
case constant3://statements
break;
}
if you want to switch with single value then it is possible
int a = 3;
if (a == 1)
{
//statements
}
else if(a == 2)
{
//statements
}
else if(a == 3)
{
//statements
}
else if(a == 4)
{
//statements
}
else
{
//statements
}
can be converted into switch as below:
int a = 3;
switch(a)
{
case 1: //statements
break;
case 2: //statements
break;
case 3: //statements
break;
case 4: //statements
break;
default : //statements
break;
}
I'm really bad at explaining things, but I'll try my best.
I'm making a small program that converts one word into another as you type. Each letter that is typed goes through this section of code where it is changed to a different letter depending on its Index position of the whole word.
My issue here is that when there are repeating letters, the letters that repeat don't change according to their position within the word but rather the first occurrence.
For example this made up word "bacca". If you put that through the code, it SHOULD change to "vrwiy" but instead it changes to "vrwwr". I know why this is too. It's because the switch statement loops through the word that needs to be converted. However I'm without a clue on how to make it change each char according to it's own individual position within the index of the string. I thought maybe the LastIndexOf() method would work but instead it just reverses the order. So if I were to type the letter "a", it would come out as "n", but if I were to type "aa", it would switch the first "a" to "r" because the second is at the IndexOf 1 get's changed to "r".
private void inputTbox_TextChanged(object sender, EventArgs e)
{
List<string> rawZnWordList = new List<string>();
foreach (char a in inputTextBox.Text)
{
switch (inputTextBox.Text.IndexOf(a))
{
case 0:
switch (a)
{
case 'a':
rawZnWordList.Add("n");
continue;
case 'b':
rawZnWordList.Add("v");
continue;
case 'c':
rawZnWordList.Add("a");
continue;
default:
break;
}
continue;
case 1:
switch (a)
{
case 'a':
rawZnWordList.Add("r");
continue;
case 'b':
rawZnWordList.Add("x");
continue;
case 'c':
rawZnWordList.Add("z");
continue;
default:
break;
}
continue;
case 2:
switch (a)
{
case 'a':
rawZnWordList.Add("t");
continue;
case 'b':
rawZnWordList.Add("l");
continue;
case 'c':
rawZnWordList.Add("w");
continue;
default:
continue;
}
continue;
case 3:
switch (a)
{
case 'a':
rawZnWordList.Add("u");
continue;
case 'b':
rawZnWordList.Add("i");
continue;
case 'c':
rawZnWordList.Add("o");
continue;
default:
break;
}
continue;
case 4:
switch (a)
{
case 'a':
rawZnWordList.Add("y");
continue;
case 'b':
rawZnWordList.Add("m");
continue;
case 'c':
rawZnWordList.Add("d");
continue;
default:
break;
}
continue;
default:
break;
}
}
string finalZnWord = string.Join("", rawZnWordList.ToArray());
outputTextBox.Text = finalZnWord;
}
You should try using a for loop instead, like this:
for (int i = 0; i < inputTextBox.Text.Length; i++)
{
char a = inputTextBox.Text[i];
switch (i)
{
case 0:
switch (a)
...
Hope this helps ;).
You need to keep track of the index inside your foreach instead of using .IndexOf. You could also use a regular for loop instead of a foreach but this way is a minimal change to your code.
private void inputTbox_TextChanged(object sender, EventArgs e)
{
List rawZnWordList = new List();
int index = 0;
foreach (char a in inputTextBox.Text)
{
switch (index)
{
case 0:
switch (a)
{
case 'a':
rawZnWordList.Add("n");
continue;
case 'b':
rawZnWordList.Add("v");
continue;
case 'c':
rawZnWordList.Add("a");
continue;
default:
break;
}
continue;
case 1:
switch (a)
{
case 'a':
rawZnWordList.Add("r");
continue;
case 'b':
rawZnWordList.Add("x");
continue;
case 'c':
rawZnWordList.Add("z");
continue;
default:
break;
}
continue;
case 2:
switch (a)
{
case 'a':
rawZnWordList.Add("t");
continue;
case 'b':
rawZnWordList.Add("l");
continue;
case 'c':
rawZnWordList.Add("w");
continue;
default:
continue;
}
continue;
case 3:
switch (a)
{
case 'a':
rawZnWordList.Add("u");
continue;
case 'b':
rawZnWordList.Add("i");
continue;
case 'c':
rawZnWordList.Add("o");
continue;
default:
break;
}
continue;
case 4:
switch (a)
{
case 'a':
rawZnWordList.Add("y");
continue;
case 'b':
rawZnWordList.Add("m");
continue;
case 'c':
rawZnWordList.Add("d");
continue;
default:
break;
}
continue;
default:
break;
}
index++;
}
string finalZnWord = string.Join("", rawZnWordList.ToArray());
outputTextBox.Text = finalZnWord;
}
I think this does the same thing and is a lot more readable. Of course replace the letter rings with your own values. I only went up to 5 characters. I'm guessing you would want more.
//replacement letter rings
char[][] aRep = {
"xfhygaodsekzcpubitlvnjqmrw".ToCharArray(),
"wqtnsepkbalmzyxvordhjgifcu".ToCharArray(),
"nyxgmcibplovkwrszaehftqjud".ToCharArray(),
"soqjhpybuwfxvartkzginemdcl".ToCharArray(),
"pldquhegkaomcnjrfxiysvtbwz".ToCharArray(),
};
private string newText(string inVal)
{
char[] ia = inVal.ToCharArray(); //in array
int l = ia.Length;
char[] oa = new char[l]; //out array
for (int i = 0; i < l; i++)
oa[i] = aRep[i][(int)ia[i]-97]; //lowercase starts at char97
return new string(oa);
}
Here is also the code I used to generate the rings:
//generate random letter rings
//char[] ascii = "abcdefghijklmnopqrstuvwxyz".ToCharArray();
//for (int i = 0; i < 8; i++)
// new string(ascii.OrderBy (x => Guid.NewGuid() ).ToArray()).Dump();
I did some very simple testing and this seemed quite a bit faster than the original approach and more importantly (to me) it is way more readable and a lot easier to see your replacement letter sequences. I think there is some more optimization to be done, but this i think is a good start. Just call this with your text during 'on change'.
I use ILSpy and .NET Reflector to see code, and I found code I don't understand it, I serach in the web and I don't found any1 can help how to resolve it.
This code with ILSpy (.NET Reflector show the same)
private void Item_Clicked(object sender, EventArgs e)
{
switch (0)
{
case 0:
{
IL_0E:
FileSystemItem fileSystemItem;
while (true)
{
fileSystemItem = (sender as FileSystemItem);
int num = 1;
while (true)
{
switch (num)
{
case 0:
if (<PrivateImplementationDetails>{4E2292E7-C82C-431F-9529-B0045F4C1457}.$$method0x60003e4-1 != null)
{
goto IL_25F;
}
if (true)
{
IL_163:
num = 3;
continue;
}
goto IL_163;
case 1:
if (fileSystemItem != null)
{
num = 11;
continue;
}
return;
case 2:
goto IL_3B8;
case 3:
{
Dictionary<string, int> expr_1B9 = new Dictionary<string, int>(9);
expr_1B9.Add(".mp3", 0);
expr_1B9.Add(".wav", 1);
expr_1B9.Add(".wma", 2);
expr_1B9.Add(".wmv", 3);
expr_1B9.Add(".avi", 4);
expr_1B9.Add(".jpg", 5);
expr_1B9.Add(".png", 6);
expr_1B9.Add(".txt", 7);
expr_1B9.Add(".inf", 8);
<PrivateImplementationDetails>{4E2292E7-C82C-431F-9529-B0045F4C1457}.$$method0x60003e4-1 = expr_1B9;
num = 15;
continue;
}
case 4:
{
string ext;
if ((ext = fileSystemItem.Ext) != null)
{
num = 8;
continue;
}
return;
}
case 5:
goto IL_2C9;
case 6:
num = 12;
continue;
case 7:
{
string ext;
int num2;
if (<PrivateImplementationDetails>{4E2292E7-C82C-431F-9529-B0045F4C1457}.$$method0x60003e4-1.TryGetValue(ext, ref num2))
{
num = 6;
continue;
}
return;
}
case 8:
num = 0;
continue;
case 9:
goto IL_25A;
case 10:
goto IL_36A;
case 11:
num = 14;
continue;
case 12:
{
int num2;
switch (num2)
{
case 0:
case 1:
case 2:
num = 16;
continue;
case 3:
case 4:
goto IL_171;
case 5:
case 6:
goto IL_1A4;
case 7:
case 8:
this.ShowText(fileSystemItem.Path);
num = 9;
continue;
default:
num = 13;
continue;
}
break;
}
case 13:
return;
case 14:
if (fileSystemItem.IsFolder)
{
switch ((1 == 1) ? 1 : 0)
{
case 0:
case 2:
goto IL_36F;
case 1:
IL_2B7:
if (false)
{
IL_2BD:
num = 5;
continue;
}
goto IL_2BD;
}
goto IL_2B7;
}
num = 4;
continue;
case 15:
goto IL_25F;
case 16:
if (fileSystemItem.IsPlaying)
{
num = 10;
continue;
}
goto IL_36F;
}
break;
IL_25F:
num = 7;
continue;
IL_36F:
BackgroundAudioPlayer.get_Instance().set_Track(new AudioTrack(new Uri(fileSystemItem.Path, 2), "Unknown", "Unknown", "Unknown", null));
IEnumerator<object> enumerator = this.filesListBox.get_Items().GetEnumerator();
num = 2;
}
}
IL_131:
BackgroundAudioPlayer.get_Instance().Stop();
fileSystemItem.IsPlaying = false;
return;
IL_171:
MediaPlayerLauncher mediaPlayerLauncher = new MediaPlayerLauncher();
mediaPlayerLauncher.set_Media(new Uri(fileSystemItem.Path, 2));
mediaPlayerLauncher.set_Location(2);
mediaPlayerLauncher.Show();
return;
IL_1A4:
this.ShowImage(fileSystemItem.Path);
return;
IL_25A:
return;
IL_2C9:
this.ShowFolderContents(fileSystemItem.Path);
return;
IL_36A:
goto IL_131;
IL_3B8:
try
{
int num = 2;
while (true)
{
switch (num)
{
case 0:
num = 1;
continue;
case 1:
goto IL_EC;
case 3:
{
IEnumerator<object> enumerator;
if (!enumerator.MoveNext())
{
num = 0;
continue;
}
FileSystemItem fileSystemItem2 = (FileSystemItem)enumerator.get_Current();
fileSystemItem2.IsPlaying = false;
num = 4;
continue;
}
}
IL_A7:
num = 3;
continue;
goto IL_A7;
}
IL_EC:
goto IL_23A;
}
finally
{
int num = 2;
while (true)
{
IEnumerator<object> enumerator;
switch (num)
{
case 0:
goto IL_12E;
case 1:
enumerator.Dispose();
num = 0;
continue;
}
if (enumerator == null)
{
break;
}
num = 1;
}
IL_12E:;
}
goto IL_131;
IL_23A:
fileSystemItem.IsPlaying = true;
return;
}
}
goto IL_0E;
}
the error of this dictionnary show me this error
<PrivateImplementationDetails>{4E2292E7-C82C-431F-9529-B0045F4C1457}.$$method0x60003e4-1
So, this what? and how Can I resolve that?
I need to replace it with another variable or what?
<PrivateImplementationDetails>{4E2292E7-C82C-431F-9529-B0045F4C1457} is not a proper C# construct. This is perhaps a substitute name which ILSpy issues to some internal framework function, which is not supposed to be used by ordinary developers.
So you need to reverse-engineer the code and understand what does it really do. And implement it by some other means. You cannot just take it and use it :)
By the way, the code seems to be an obfuscated one, while there are lots of unnecessary instructions there. In your case, the whole loop/switch codes a state machine in a fancy way. (It could be that its execution is always sequential, so you ought to try tracing it manually -- with paper and pencil. Try it, it's fun!)
How to hilight pair brackets ( "{" and "}" ) in Scintilla.net?
You can set the IsBraceMatching property of your ScintillaNet control to true and it will highlight (), [] and {}.
I don't know which version of ScintillaNet you are using but this should help:
https://github.com/jacobslusser/ScintillaNET/wiki/Brace-Matching
With highlight to:
int lastCaretPos = 0;
private void scintilla_UpdateUI(object sender, UpdateUIEventArgs e)
{
// Has the caret changed position?
var caretPos = scintilla.CurrentPosition;
if (lastCaretPos != caretPos)
{
lastCaretPos = caretPos;
var bracePos1 = -1;
var bracePos2 = -1;
// Is there a brace to the left or right?
if (caretPos > 0 && IsBrace(scintilla.GetCharAt(caretPos - 1)))
bracePos1 = (caretPos - 1);
else if (IsBrace(scintilla.GetCharAt(caretPos)))
bracePos1 = caretPos;
if (bracePos1 >= 0)
{
// Find the matching brace
bracePos2 = scintilla.BraceMatch(bracePos1);
if (bracePos2 == Scintilla.InvalidPosition)
scintilla.BraceBadLight(bracePos1);
else
scintilla.BraceHighlight(bracePos1, bracePos2);
}
else
{
// Turn off brace matching
scintilla.BraceHighlight(Scintilla.InvalidPosition, Scintilla.InvalidPosition);
}
}
}
private static bool IsBrace(int c)
{
switch (c)
{
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case '<':
case '>':
return true;
}
return false;
}