Cleaned up some code,
Some checks failed
continuous-integration/appveyor/branch AppVeyor build failed
Some checks failed
continuous-integration/appveyor/branch AppVeyor build failed
Fixed append operator requirement (Closes #10)
This commit is contained in:
parent
65a7b0e497
commit
17d4a3672b
@ -35,8 +35,6 @@ namespace Assignment_1
|
|||||||
'\''
|
'\''
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool nonStrict = false;
|
|
||||||
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine("┌──────────────────────────────────────────┐");
|
Console.WriteLine("┌──────────────────────────────────────────┐");
|
||||||
@ -50,20 +48,14 @@ namespace Assignment_1
|
|||||||
MemoryStream sourceStream = new(1024);
|
MemoryStream sourceStream = new(1024);
|
||||||
Parser parser = new();
|
Parser parser = new();
|
||||||
bool dynamicInput = false;
|
bool dynamicInput = false;
|
||||||
/*foreach (var arg in args)
|
|
||||||
{
|
|
||||||
if (arg == "-ns")
|
|
||||||
{
|
|
||||||
nonStrict = true;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
// From https://stackoverflow.com/questions/3453220/how-to-detect-if-console-in-stdin-has-been-redirected
|
// From https://stackoverflow.com/questions/3453220/how-to-detect-if-console-in-stdin-has-been-redirected
|
||||||
// Reading from pipes is equivalent to reading user input, though the input is redirected
|
// Reading from pipes is equivalent to reading user input, though the input is redirected
|
||||||
if (Console.IsInputRedirected)
|
if (Console.IsInputRedirected)
|
||||||
{
|
{
|
||||||
// To simplify reading, we read all input bytes from the piped input to the stream.
|
// To simplify reading, we read all input bytes from the piped input to the stream.
|
||||||
// This is by far not the best way to do it; reading line-by-line would reduce memory space,
|
// Not the best way to do it; we don't need to keep any data that has already been read.
|
||||||
// but it allows a simple read into the console
|
// Whilst the stream could be copied excluding already parsed data at each input step, this would rely
|
||||||
|
// on GC to cleanup afterwards
|
||||||
sourceStream.Write(Encoding.UTF8.GetBytes(Console.In.ReadToEnd()));
|
sourceStream.Write(Encoding.UTF8.GetBytes(Console.In.ReadToEnd()));
|
||||||
// Dispose will close a piped input, or piped file in further iterations of the program
|
// Dispose will close a piped input, or piped file in further iterations of the program
|
||||||
Console.In.Dispose();
|
Console.In.Dispose();
|
||||||
@ -157,174 +149,159 @@ namespace Assignment_1
|
|||||||
public void StartParsing(Stream source, bool dynamicInput = false)
|
public void StartParsing(Stream source, bool dynamicInput = false)
|
||||||
{
|
{
|
||||||
long initSourceLength = source.Length;
|
long initSourceLength = source.Length;
|
||||||
//if (nonStrict || PeekChar(source) == '{')
|
long lastLinePos = 0;
|
||||||
if (true)
|
long initPos = 0;
|
||||||
|
bool cont = false;
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
/*if (PeekChar(source) == '{')
|
if (dynamicInput)
|
||||||
{
|
{
|
||||||
source.ReadByte();
|
lastLinePos = source.Position;
|
||||||
}*/
|
|
||||||
long lastLinePos = 0;
|
|
||||||
long initPos = 0;
|
|
||||||
bool cont = false;
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (dynamicInput)
|
|
||||||
{
|
|
||||||
lastLinePos = source.Position;
|
|
||||||
if (!cont)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Enter a command: ");
|
|
||||||
}
|
|
||||||
string s = Console.ReadLine();
|
|
||||||
long pos = source.Position;
|
|
||||||
source.Write(Encoding.UTF8.GetBytes(s));
|
|
||||||
source.Position = pos;
|
|
||||||
|
|
||||||
}
|
|
||||||
// parse the statement or list of statements;
|
|
||||||
// This is done by reading the next word
|
|
||||||
if (!cont)
|
if (!cont)
|
||||||
{
|
{
|
||||||
initPos = source.Position;
|
Console.WriteLine("Enter a command: ");
|
||||||
|
}
|
||||||
|
string s = Console.ReadLine();
|
||||||
|
long pos = source.Position;
|
||||||
|
source.Write(Encoding.UTF8.GetBytes(s));
|
||||||
|
source.Position = pos;
|
||||||
|
|
||||||
|
}
|
||||||
|
// parse the statement or list of statements;
|
||||||
|
// This is done by reading the next word
|
||||||
|
if (!cont)
|
||||||
|
{
|
||||||
|
initPos = source.Position;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
source.Position = initPos;
|
||||||
|
}
|
||||||
|
SkipWhitespace(source);
|
||||||
|
long position = FindNextWord(source, out string word);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Enum.TryParse(typeof(Statements), word, out object statementType))
|
||||||
|
{
|
||||||
|
// By turning the result of the command into an action,
|
||||||
|
// we can defer processing the final result until the end of this control flow
|
||||||
|
Action result = () => { };
|
||||||
|
source.Position = position;
|
||||||
|
switch ((Statements)statementType)
|
||||||
|
{
|
||||||
|
case Statements.exit:
|
||||||
|
result = Exit(source, initSourceLength, dynamicInput);
|
||||||
|
break;
|
||||||
|
case Statements.append:
|
||||||
|
result = AppendSet(source);
|
||||||
|
break;
|
||||||
|
case Statements.list:
|
||||||
|
long pos = FindNextWord(source, out string nextWord);
|
||||||
|
if (nextWord == "all")
|
||||||
|
{
|
||||||
|
source.Position = pos;
|
||||||
|
result = List(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = List();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Statements.print:
|
||||||
|
result = Print(source, 0);
|
||||||
|
break;
|
||||||
|
case Statements.printlength:
|
||||||
|
result = Print(source, 1);
|
||||||
|
break;
|
||||||
|
case Statements.printwords:
|
||||||
|
result = Print(source, 2);
|
||||||
|
break;
|
||||||
|
case Statements.printwordcount:
|
||||||
|
result = Print(source, 3);
|
||||||
|
break;
|
||||||
|
case Statements.set:
|
||||||
|
result = AppendSet(source, false);
|
||||||
|
break;
|
||||||
|
case Statements.reverse:
|
||||||
|
result = Reverse(source);
|
||||||
|
break;
|
||||||
|
// These are additional helper functions. Thier input gets excluded from the MemoryStream
|
||||||
|
case Statements.h:
|
||||||
|
Console.WriteLine("Commands are: ");
|
||||||
|
foreach (var item in Enum.GetValues(typeof(Statements)))
|
||||||
|
{
|
||||||
|
Console.WriteLine("\t{0}", ((Statements)item).ToString());
|
||||||
|
}
|
||||||
|
// Ignore these as actual commands
|
||||||
|
source.Position = initPos;
|
||||||
|
source.SetLength(initPos);
|
||||||
|
break;
|
||||||
|
case Statements.writeout:
|
||||||
|
// Writes the full command history to the stream.
|
||||||
|
Console.WriteLine("Writing input commands to {0}...");
|
||||||
|
source.Position = initPos;
|
||||||
|
source.SetLength(initPos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Do a check semicolons etc
|
||||||
|
if (IsNextEoS(source))
|
||||||
|
{
|
||||||
|
// Increment the source pos past the semi-colon
|
||||||
|
cont = false;
|
||||||
|
source.Position++;
|
||||||
|
if (dynamicInput)
|
||||||
|
{
|
||||||
|
// Nicely format the output stream, so we may print it cleanly
|
||||||
|
source.WriteByte((byte)'\n');
|
||||||
|
}
|
||||||
|
result();
|
||||||
|
if (((Statements)statementType).Equals(Statements.exit))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (source.Position != lastLinePos - 1)
|
||||||
|
{
|
||||||
|
// In the case that we expect some more data, we must keep tabs of our current line, and keep accumulating data until we're finished
|
||||||
|
lastLinePos = source.Position;
|
||||||
|
cont = true;
|
||||||
|
source.WriteByte((byte)' ');
|
||||||
|
Console.Write(">");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ParserException("expected a semi-colon", 0, source.Position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
source.Position = initPos;
|
throw new ParserException("Failed parsing statement", 0, source.Position);
|
||||||
}
|
}
|
||||||
SkipWhitespace(source);
|
}
|
||||||
long position = FindNextWord(source, out string word);
|
// Throwing a parserexception will return us to this point immediately. From here, the line is automatically restored,
|
||||||
try
|
// and the excepion printed to the console window.
|
||||||
{
|
// This means that each function does not need to keep track of our current position in the stream
|
||||||
if (Enum.TryParse(typeof(Statements), word, out object statementType))
|
catch (ParserException e)
|
||||||
{
|
{
|
||||||
// By turning the result of the command into an action,
|
if (e.Importance > 3)
|
||||||
// we can defer processing the final result until the end of this control flow
|
{
|
||||||
// I.e. "I don't know what action to do, but I will need it, when I know where this statement ends"
|
throw new ApplicationException("A critical error occurred.");
|
||||||
|
}
|
||||||
// In some ways, it makes more sense. The action is determined by the interpreter's result
|
if (e.LinePosition > 0)
|
||||||
Action result = () => { };
|
{
|
||||||
source.Position = position;
|
WriteDebugLine(initPos, e.LinePosition, e.Message, source);
|
||||||
switch ((Statements)statementType)
|
}
|
||||||
{
|
else
|
||||||
case Statements.exit:
|
{
|
||||||
result = Exit(source, initSourceLength, dynamicInput);
|
Console.WriteLine(e.LinePosition + ": " + e.Message);
|
||||||
break;
|
source.Position = initPos;
|
||||||
case Statements.append:
|
source.SetLength(initPos);
|
||||||
result = AppendSet(source);
|
}
|
||||||
break;
|
if (!dynamicInput)
|
||||||
case Statements.list:
|
{
|
||||||
long pos = FindNextWord(source, out string nextWord);
|
Environment.Exit(-1);
|
||||||
if (nextWord == "all")
|
|
||||||
{
|
|
||||||
source.Position = pos;
|
|
||||||
result = List(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = List();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Statements.print:
|
|
||||||
result = Print(source, 0);
|
|
||||||
break;
|
|
||||||
case Statements.printlength:
|
|
||||||
result = Print(source, 1);
|
|
||||||
break;
|
|
||||||
case Statements.printwords:
|
|
||||||
result = Print(source, 2);
|
|
||||||
break;
|
|
||||||
case Statements.printwordcount:
|
|
||||||
result = Print(source, 3);
|
|
||||||
break;
|
|
||||||
case Statements.set:
|
|
||||||
result = AppendSet(source, false);
|
|
||||||
break;
|
|
||||||
case Statements.reverse:
|
|
||||||
result = Reverse(source);
|
|
||||||
break;
|
|
||||||
// These are additional helper functions. Thier input gets excluded from the MemoryStream
|
|
||||||
case Statements.h:
|
|
||||||
Console.WriteLine("Commands are: ");
|
|
||||||
foreach (var item in Enum.GetValues(typeof(Statements)))
|
|
||||||
{
|
|
||||||
Console.WriteLine("\t{0}", ((Statements)item).ToString());
|
|
||||||
}
|
|
||||||
// Ignore these as actual commands
|
|
||||||
source.Position = initPos;
|
|
||||||
source.SetLength(initPos);
|
|
||||||
break;
|
|
||||||
case Statements.writeout:
|
|
||||||
// Writes the full command history to the stream.
|
|
||||||
Console.WriteLine("Writing input commands to {0}...");
|
|
||||||
source.Position = initPos;
|
|
||||||
source.SetLength(initPos);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Do a check semicolons etc
|
|
||||||
if (IsNextEoS(source))
|
|
||||||
{
|
|
||||||
// Increment the source pos past the semi-colon
|
|
||||||
cont = false;
|
|
||||||
source.Position++;
|
|
||||||
if (dynamicInput)
|
|
||||||
{
|
|
||||||
// Nicely format the output stream, so we may print it cleanly
|
|
||||||
source.WriteByte((byte)'\n');
|
|
||||||
}
|
|
||||||
result();
|
|
||||||
if (((Statements)statementType).Equals(Statements.exit))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (source.Position != lastLinePos - 1)
|
|
||||||
{
|
|
||||||
// In the case that we expect some more data, we must keep tabs of our current line, and keep accumulating data until we're finished
|
|
||||||
lastLinePos = source.Position;
|
|
||||||
cont = true;
|
|
||||||
source.WriteByte((byte)' ');
|
|
||||||
Console.Write(">");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ParserException("expected a semi-colon", 0, source.Position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ParserException("Failed parsing statement", 0, source.Position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Throwing a parserexception will return us to this point immediately. From here, the line is automatically restored,
|
|
||||||
// and the excepion printed to the console window.
|
|
||||||
// This means that each function does not need to keep track of our current position in the stream
|
|
||||||
catch (ParserException e)
|
|
||||||
{
|
|
||||||
if (e.Importance > 3)
|
|
||||||
{
|
|
||||||
throw new ApplicationException("A critical error occurred.");
|
|
||||||
}
|
|
||||||
if (e.LinePosition > 0)
|
|
||||||
{
|
|
||||||
WriteDebugLine(initPos, e.LinePosition, e.Message, source);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine(e.LinePosition + ": " + e.Message);
|
|
||||||
source.Position = initPos;
|
|
||||||
source.SetLength(initPos);
|
|
||||||
}
|
|
||||||
if (!dynamicInput)
|
|
||||||
{
|
|
||||||
Environment.Exit(-1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine("First read character was not \'{\'. Use the launch flag -ns for non-strict syntax checking");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,8 +409,14 @@ namespace Assignment_1
|
|||||||
for (int i = 0; i < eligibleKeys.Count; i++)
|
for (int i = 0; i < eligibleKeys.Count; i++)
|
||||||
{
|
{
|
||||||
string entryFormat = "│{0," + -1 * keyWidth + "}│{1," + -1 * valueWidth + "}│{2," + -1 * flagWidth + "}│\n";
|
string entryFormat = "│{0," + -1 * keyWidth + "}│{1," + -1 * valueWidth + "}│{2," + -1 * flagWidth + "}│\n";
|
||||||
|
List<string> keyLines = GetStringLines(eligibleKeys[i], keyWidth);
|
||||||
|
List<string> valueLines = GetStringLines(Symbols[eligibleKeys[i]].Item1.Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t"), valueWidth);
|
||||||
|
|
||||||
consoleOutput.Append(string.Format(entryFormat, eligibleKeys[i], Symbols[eligibleKeys[i]].Item1.Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t"), Convert.ToString((byte)Symbols[eligibleKeys[i]].Item2, 2).PadLeft(8, '0')));
|
for (int j = 0; j < (keyLines.Count > valueLines.Count ? keyLines.Count : valueLines.Count); j++)
|
||||||
|
{
|
||||||
|
consoleOutput.Append(string.Format(entryFormat, j >= keyLines.Count ? "" : keyLines[j], j >= valueLines.Count ? "" : valueLines[j], j == 0 ? Convert.ToString((byte)Symbols[eligibleKeys[i]].Item2, 2).PadLeft(8, '0'): ""));
|
||||||
|
}
|
||||||
|
//consoleOutput.Append(string.Format(entryFormat, eligibleKeys[i], Symbols[eligibleKeys[i]].Item1.Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t"), Convert.ToString((byte)Symbols[eligibleKeys[i]].Item2, 2).PadLeft(8, '0')));
|
||||||
if (i + 1 < eligibleKeys.Count)
|
if (i + 1 < eligibleKeys.Count)
|
||||||
{
|
{
|
||||||
consoleOutput.Append(string.Format("├" + new string('─', keyWidth) + "┼" + new string('─', valueWidth) + "┼" + new string('─', flagWidth) + "┤\n"));
|
consoleOutput.Append(string.Format("├" + new string('─', keyWidth) + "┼" + new string('─', valueWidth) + "┼" + new string('─', flagWidth) + "┤\n"));
|
||||||
@ -558,11 +541,13 @@ namespace Assignment_1
|
|||||||
{
|
{
|
||||||
string result = "";
|
string result = "";
|
||||||
// iterate through values until we reach either the end of the stream or the end-of-statement
|
// iterate through values until we reach either the end of the stream or the end-of-statement
|
||||||
|
bool IsAppendSet = true;
|
||||||
while (s.Position < s.Length && !IsNextEoS(s))
|
while (s.Position < s.Length && !IsNextEoS(s))
|
||||||
{
|
{
|
||||||
if (IsNextEoS(s, '+'))
|
if (IsNextEoS(s, '+'))
|
||||||
{
|
{
|
||||||
s.Position = FindNextWord(s, out _);
|
s.Position = FindNextWord(s, out _);
|
||||||
|
IsAppendSet = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -571,8 +556,17 @@ namespace Assignment_1
|
|||||||
{
|
{
|
||||||
Console.WriteLine("Could not parse value");
|
Console.WriteLine("Could not parse value");
|
||||||
}
|
}
|
||||||
s.Position = val;
|
if (IsAppendSet)
|
||||||
result += value;
|
{
|
||||||
|
s.Position = val;
|
||||||
|
result += value;
|
||||||
|
IsAppendSet = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ParserException("Append operator not set", 0, s.Position);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expression = result;
|
expression = result;
|
||||||
@ -701,8 +695,7 @@ namespace Assignment_1
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
static string GetNextLine(Stream s)
|
static string GetNextLine(Stream s)
|
||||||
{
|
{
|
||||||
string nextLine;
|
FindNextOccurance(s, '\n', out string nextLine);
|
||||||
FindNextOccurance(s, '\n', out nextLine);
|
|
||||||
return nextLine;
|
return nextLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -718,7 +711,7 @@ namespace Assignment_1
|
|||||||
// Record our current position
|
// Record our current position
|
||||||
long start = s.Position;
|
long start = s.Position;
|
||||||
// Check if the character at the current pos is whitespace, if so, keep advancing until it isn't.
|
// Check if the character at the current pos is whitespace, if so, keep advancing until it isn't.
|
||||||
// NB: Whitespace includes carriage returns or line feeds,
|
// NB: Whitespace includes carriage returns and line feeds,
|
||||||
// so 'set\r\n
|
// so 'set\r\n
|
||||||
// var
|
// var
|
||||||
// "expression";
|
// "expression";
|
||||||
@ -900,6 +893,18 @@ namespace Assignment_1
|
|||||||
string result = string.Format(t, source[..rightHalf], source[rightHalf..]);
|
string result = string.Format(t, source[..rightHalf], source[rightHalf..]);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static List<string> GetStringLines(string source, int maxWidth)
|
||||||
|
{
|
||||||
|
List<string> lines = new();
|
||||||
|
for (int i = 0; i < source.Length; i++)
|
||||||
|
{
|
||||||
|
int max = i + maxWidth <= source.Length ? i + maxWidth : source.Length;
|
||||||
|
lines.Add(source[i..max]);
|
||||||
|
i = max;
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
public class ParserException : Exception
|
public class ParserException : Exception
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user