// This switch follows more or less a DEA to this EBNF // COMMAND-EBNF := COMMAND // COMMAND := (!ARGUMENT \s+ (ARGUMENT|\s)*) // ARGUMENT := COMMAND|FREESTRING|QUOTESTRING // FREESTRING := [^)]+ // QUOTESTRING := "[<anything but ", \" is ok>]+" public static ASTNode ParseCommandRequest(string request) { ASTCommand root = null; var comAst = new Stack<ASTCommand>(); BuildStatus build = BuildStatus.ParseCommand; var strb = new StringBuilder(); var strPtr = new StringPtr(request); while (!strPtr.End) { ASTCommand buildCom; switch (build) { case BuildStatus.ParseCommand: // Got a command buildCom = new ASTCommand(); // Consume CommandChar if left over if (strPtr.Char == CommandChar) strPtr.Next(CommandChar); if (root == null) root = buildCom; else comAst.Peek().Parameter.Add(buildCom); comAst.Push(buildCom); build = BuildStatus.SelectParam; break; case BuildStatus.SelectParam: strPtr.SkipSpace(); if (strPtr.End) build = BuildStatus.End; else { switch (strPtr.Char) { case '"': build = BuildStatus.ParseQuotedString; break; case '(': if (!strPtr.HasNext) build = BuildStatus.ParseFreeString; else if (strPtr.IsNext(CommandChar)) { strPtr.Next('('); build = BuildStatus.ParseCommand; } else build = BuildStatus.ParseFreeString; break; case ')': if (!comAst.Any()) build = BuildStatus.End; else { comAst.Pop(); if (!comAst.Any()) build = BuildStatus.End; } strPtr.Next(); break; default: build = BuildStatus.ParseFreeString; break; } } break; case BuildStatus.ParseFreeString: strb.Clear(); var valFreeAst = new ASTValue(); using (strPtr.TrackNode(valFreeAst)) { for (; !strPtr.End; strPtr.Next()) { if ((strPtr.Char == '(' && strPtr.HasNext && strPtr.IsNext(CommandChar)) || strPtr.Char == ')' || char.IsWhiteSpace(strPtr.Char)) break; strb.Append(strPtr.Char); } } valFreeAst.Value = strb.ToString(); buildCom = comAst.Peek(); buildCom.Parameter.Add(valFreeAst); build = BuildStatus.SelectParam; break; case BuildStatus.ParseQuotedString: strb.Clear(); strPtr.Next('"'); var valQuoAst = new ASTValue(); using (strPtr.TrackNode(valQuoAst)) { bool escaped = false; for (; !strPtr.End; strPtr.Next()) { if (strPtr.Char == '\\') escaped = true; else if (strPtr.Char == '"') { if (escaped) strb.Length--; else { strPtr.Next(); break; } escaped = false; } else escaped = false; strb.Append(strPtr.Char); } } valQuoAst.Value = strb.ToString(); buildCom = comAst.Peek(); buildCom.Parameter.Add(valQuoAst); build = BuildStatus.SelectParam; break; case BuildStatus.End: strPtr.JumpToEnd(); break; default: throw new InvalidOperationException(); } } return root; }
// This switch follows more or less a DEA to this EBNF // COMMAND-EBNF := COMMAND // COMMAND := (!ARGUMENT \s+ (ARGUMENT|\s)*) // ARGUMENT := COMMAND|FREESTRING|QUOTESTRING // FREESTRING := [^)]+ // QUOTESTRING := "[<anything but ", \" is ok>]+" public static ASTNode ParseCommandRequest(string request, char commandChar = DefaultCommandChar, char delimeterChar = DefaultDelimeterChar) { ASTCommand root = null; var comAst = new Stack <ASTCommand>(); BuildStatus build = BuildStatus.ParseCommand; var strb = new StringBuilder(); var strPtr = new StringPtr(request); while (!strPtr.End) { ASTCommand buildCom; switch (build) { case BuildStatus.ParseCommand: // Got a command buildCom = new ASTCommand(); // Consume CommandChar if left over if (strPtr.Char == commandChar) { strPtr.Next(commandChar); } if (root == null) { root = buildCom; } else { comAst.Peek().Parameter.Add(buildCom); } comAst.Push(buildCom); build = BuildStatus.SelectParam; break; case BuildStatus.SelectParam: strPtr.SkipChar(delimeterChar); if (strPtr.End) { build = BuildStatus.End; } else { switch (strPtr.Char) { case '"': build = BuildStatus.ParseQuotedString; //goto case BuildStatus.ParseQuotedString; break; case '(': if (!strPtr.HasNext) { build = BuildStatus.ParseFreeString; } else if (strPtr.IsNext(commandChar)) { strPtr.Next('('); build = BuildStatus.ParseCommand; } else { build = BuildStatus.ParseFreeString; } break; case ')': if (!comAst.Any()) { build = BuildStatus.End; } else { comAst.Pop(); if (!comAst.Any()) { build = BuildStatus.End; } } strPtr.Next(); break; default: build = BuildStatus.ParseFreeString; break; } } break; case BuildStatus.ParseFreeString: strb.Clear(); var valFreeAst = new ASTValue(); using (strPtr.TrackNode(valFreeAst)) { for (; !strPtr.End; strPtr.Next()) { if ((strPtr.Char == '(' && strPtr.HasNext && strPtr.IsNext(commandChar)) || strPtr.Char == ')' || strPtr.Char == delimeterChar) { break; } strb.Append(strPtr.Char); } } valFreeAst.Value = strb.ToString(); buildCom = comAst.Peek(); buildCom.Parameter.Add(valFreeAst); build = BuildStatus.SelectParam; break; case BuildStatus.ParseQuotedString: strb.Clear(); strPtr.Next('"'); var valQuoAst = new ASTValue(); using (strPtr.TrackNode(valQuoAst)) { bool escaped = false; for (; !strPtr.End; strPtr.Next()) { if (strPtr.Char == '\\') { escaped = true; } else if (strPtr.Char == '"') { if (escaped) { strb.Length--; } else { strPtr.Next(); break; } escaped = false; } else { escaped = false; } strb.Append(strPtr.Char); } } valQuoAst.Value = strb.ToString(); buildCom = comAst.Peek(); buildCom.Parameter.Add(valQuoAst); build = BuildStatus.SelectParam; break; case BuildStatus.End: strPtr.JumpToEnd(); break; default: throw new InvalidOperationException(); } } return(root); }