ShiftOS_TheReturn/ShiftOS_TheReturn/CommandParser.cs
Michael 8fe8026de5 Merge remote-tracking branch 'origin/monogame' into monogame
# Conflicts:
#	ShiftOS.Frontend/Apps/ChatClient.cs
2017-08-03 09:46:48 -04:00

474 lines
14 KiB
C#

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ShiftOS.Engine
{
/// <summary>
/// Provides functionality for parsing a Terminal command string
/// </summary>
public sealed class CommandParser
{
public IList<CommandFormat> parts = new List<CommandFormat>();
/// <summary>
/// Adds a command format to the parser
/// </summary>
/// <param name="part">The format to add</param>
public void AddPart(CommandFormat part)
{
parts.Add(part);
}
/// <summary>
/// Imports the command parser formats from a list.
/// </summary>
/// <param name="parts">The list of formats.</param>
public void ImportPart(IList<CommandFormat> parts)
{
this.parts = parts;
}
/// <summary>
/// Saves the parser to a JSON string.
/// </summary>
/// <returns></returns>
public string Save()
{
return JsonConvert.SerializeObject(parts, Formatting.Indented);
}
/// <summary>
/// Loads a new <see cref="CommandParser"/> from a file.
/// </summary>
/// <param name="val">The file to load</param>
/// <returns>The parser constructed from the file</returns>
public static CommandParser Load(string val)
{
CommandParser parser = new CommandParser();
JArray data = JArray.Parse(val);
IList<CFValue> values = data.Select(obj => new CFValue(
(string)obj["type"],
(string)obj["text"]
)).ToList();
foreach (CFValue value in values)
{
parser.AddPart(value.GetCommandFormat());
}
return parser;
}
/// <summary>
/// Parses a command string.
/// </summary>
/// <param name="cdd">The command string to parse.</param>
/// <returns>The parsed command, ready to be invoked.</returns>
public KeyValuePair<string, Dictionary<string, string>> ParseCommand(string cdd)
{
string command = "";
Dictionary<string, string> arguments = new Dictionary<string, string>();
string text = cdd;
int position = 0;
int commandPos;
int firstValuePos = -1;
int lastValuePos = -1;
int firstTextPart = -1;
for (int ii = 0; ii < parts.Count; ii++)
{
CommandFormat part = parts[ii];
if(part is CommandFormatText)
{
if(firstTextPart < 0)
firstTextPart = ii;
}
if (part is CommandFormatMarker)
{
if (part is CommandFormatCommand)
{
commandPos = ii;
}
else if (part is CommandFormatValue)
{
if (firstValuePos > -1)
lastValuePos = ii;
else
firstValuePos = ii;
}
}
}
int i = 0;
string currentArgument = "";
int help = -1;
bool id_found = false;
string id_text = "";
while (position < text.Length)
{
if (i >= parts.Count)
{
i = 1;
}
var part = parts[i];
string inp = text.Substring(position);
string res = part.CheckValidity(inp);
if (part is CommandFormatText)
{
if (res == "+FALSE+")
{
if (id_found == false)
{
id_found = true;
currentArgument = "id";
if (!arguments.ContainsKey("id"))
{
arguments.Add("id", "");
i = firstValuePos;
position++;
continue;
}
else
{
Console.WriteLine("Duplicate command-line argument detected: id");
command = "+FALSE+";
position = text.Length;
}
}
}
}
// ok so:
// example
// COMMAND text[ --] ARGUMENT VALUE text[ --] ARGUMENT VALUE
// COMMAND text[{] ARGUMENT text[=] VALUE text[, ] ARGUMENT text[=] VALUE text[}]
if (part is CommandFormatMarker)
{
if (part is CommandFormatCommand)
{
command = res;
help = -1;
}
else if (part is CommandFormatArgument)
{
if (arguments.ContainsKey(res))
{
Console.WriteLine("Duplicate command-line argument detected: " + res);
command = "+FALSE+";
position = text.Length;
}
else
{
currentArgument = res;
help = -1;
}
}
else if (part is CommandFormatValue)
{
arguments[currentArgument] = string.Join("", res.Split('"'));
if (i == firstValuePos)
help = lastValuePos;
if (i == lastValuePos)
help = firstValuePos;
}
}
if (res == "+FALSE+")
{
if (help > -1)
{
i = help;
if (i >= parts.Count)
{
position = text.Length;
command = "+FALSE+";
}
}
else
{
position = text.Length;
command = "+FALSE+";
}
help = -1;
}
else
{
position += res.Length;
}
i++;
}
if (command == "+FALSE+")
{
//lblExampleCommand.Text = "Syntax Error";
return new KeyValuePair<string, Dictionary<string, string>>();
}
else
{
/*string argvs = "{";
foreach (KeyValuePair<string, string> entry in arguments) {
argvs += entry.Key + "=" + entry.Value + ", ";
}
argvs += "}";
lblExampleCommand.Text = command + argvs;*/
return new KeyValuePair<string, Dictionary<string, string>>(command, arguments);
}
}
public static CommandParser GenerateSample()
{
var parser = new CommandParser();
parser.AddPart(new CommandFormatCommand());
parser.AddPart(new CommandFormatText(" --"));
parser.AddPart(new CommandFormatArgument());
parser.AddPart(new CommandFormatText(" "));
parser.AddPart(new CommandFormatValue());
parser.AddPart(new CommandFormatText(" --"));
parser.AddPart(new CommandFormatArgument());
parser.AddPart(new CommandFormatText(" "));
parser.AddPart(new CommandFormatValue());
return parser;
}
}
public class CFValue
{
public string type { get; set; }
public string text { get; set; }
public CFValue(string type, string text)
{
this.type = type;
this.text = text;
}
public CFValue(CommandFormat format)
{
type = "";
text = "";
if (format is CommandFormatText)
{
text = ((CommandFormatText)format).str;
if (format is CommandFormatOptionalText)
{
type = "optionalText";
}
else if (format is CommandFormatRegex)
{
type = "regexText";
}
else
{
type = "text";
}
}
else if (format is CommandFormatMarker)
{
if (format is CommandFormatCommand)
{
type = "command";
}
else if (format is CommandFormatArgument)
{
type = "argument";
}
else if (format is CommandFormatValue)
{
type = "value";
}
}
}
public CommandFormat GetCommandFormat()
{ // TODO update with better code
switch (type)
{
case "text":
return new CommandFormatText(text);
case "optionalText":
return new CommandFormatOptionalText(text);
case "regexText":
return new CommandFormatRegex(text);
case "command":
return new CommandFormatCommand();
case "argument":
return new CommandFormatArgument();
case "value":
return new CommandFormatValue();
case "color":
throw new NotImplementedException(); // fix this (make it not a notimplementedexception)
}
return new CommandFormatMarker();
}
}
public class CommandFormat
{
public virtual string CheckValidity(string check)
{
return check;
}
//winforms use in engine - coding standard violation.
//Control Draw();
}
public class CommandFormatText : CommandFormat
{
public string str = "";
TextBox textBox;
public CommandFormatText()
{
}
public CommandFormatText(string str)
{
this.str = str;
}
public override string CheckValidity(string check)
{
return check.StartsWith(str) ? str : "+FALSE+";
}
void TextChanged(object sender, EventArgs e)
{
str = textBox.Text;
}
}
public class CommandFormatOptionalText : CommandFormatText
{
public CommandFormatOptionalText() : base()
{
}
public CommandFormatOptionalText(string str) : base(str)
{
}
public override string CheckValidity(string check)
{
return check.StartsWith(str) ? str : "";
}
}
public class CommandFormatRegex : CommandFormatText
{
public CommandFormatRegex() : base()
{
}
public CommandFormatRegex(string str) : base(str)
{
}
public override string CheckValidity(string check)
{
Match match = (new Regex("^" + str)).Match(check);
return match.Success ? match.Value : "+FALSE+";
}
}
public class CommandFormatMarker : CommandFormat
{
protected string str;
Button button;
public CommandFormatMarker()
{
}
public override string CheckValidity(string check)
{
string res = string.Empty;
string alphanumeric = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm-_0123456789"; // not using regex for performance reasons
//You said "alphanumeric" for that variable name... but it only had the alphabet in it. Rectified. - Michael
foreach (char c in check)
{
if (alphanumeric.IndexOf(c) > -1)
{
res += c;
}
else
{
break;
}
}
return res;
}
}
public class CommandFormatCommand : CommandFormatMarker
{
//stub
}
public class CommandFormatArgument : CommandFormatMarker
{
//stub
}
public class CommandFormatValue : CommandFormatMarker
{
public override string CheckValidity(string cd)
{
string res = string.Empty;
var check = "";
bool done = false;
if (cd.StartsWith("\""))
{
check = cd.Substring(1);
foreach (char c in check)
{
if (c != '"')
{
res += c;
}
else
{
done = true;
res = "\"" + res + "\"";
break;
}
}
}
else
{
res = base.CheckValidity(cd);
done = true;
}
return done ? res : "+FALSE+";
}
}
}