diff --git a/Assignment 1/Assignment 1.csproj b/Assignment 1/Assignment 1.csproj
index 8f9a35c..e02a622 100644
--- a/Assignment 1/Assignment 1.csproj
+++ b/Assignment 1/Assignment 1.csproj
@@ -6,10 +6,6 @@
Assignment_1
-
-
-
-
diff --git a/Assignment 1/Examples/test1.txt b/Assignment 1/Examples/test1.txt
new file mode 100644
index 0000000..e83052b
--- /dev/null
+++ b/Assignment 1/Examples/test1.txt
@@ -0,0 +1,3 @@
+set apple "Apple trees are small.";
+reverse apple;
+print apple;
\ No newline at end of file
diff --git a/Assignment 1/Program.cs b/Assignment 1/Program.cs
index ced166b..3fef73d 100644
--- a/Assignment 1/Program.cs
+++ b/Assignment 1/Program.cs
@@ -22,6 +22,8 @@ namespace Assignment_1
NoPrint = 2,
Static = 4
}
+
+ // Max display width for tables etc.
static readonly int ConsoleWidthLimit = 80;
static void Main(string[] args)
@@ -42,7 +44,8 @@ namespace Assignment_1
if (Console.IsInputRedirected || loadedFromFile)
{
// To simplify reading, we read all input bytes from the piped input to the stream.
- // Not the best way to do it; we don't need to keep any data that has already been read and parsed successfully.
+ // Not the best way to do it; we don't need to keep any data that has already been read and parsed successfully,
+ // and we could avoid copying the supplied stream. But keeps interactivity with the console simple
sourceStream.Write(Encoding.UTF8.GetBytes(Console.In.ReadToEnd()));
Console.In.Dispose();
Console.SetIn(new StreamReader(Console.OpenStandardInput()));
@@ -75,7 +78,6 @@ namespace Assignment_1
}
else
{
- // Need the logic to prep the next source stream
ck = new ConsoleKeyInfo();
while (ck.Key != ConsoleKey.Y && ck.Key != ConsoleKey.N)
{
@@ -131,7 +133,6 @@ namespace Assignment_1
set,
reverse,
h,
- writeout
}
public void StartParsing(Stream source, bool dynamicInput = false)
{
@@ -155,8 +156,6 @@ namespace Assignment_1
source.Position = pos;
}
- // parse the statement or list of statements;
- // This is done by reading the next word
if (!cont)
{
initPos = source.Position;
@@ -213,22 +212,19 @@ namespace Assignment_1
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
+ // Stream ignores this command
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++;
SkipWhitespace(source);
@@ -236,8 +232,7 @@ namespace Assignment_1
else isLineFinished = false;
if (dynamicInput && isLineFinished)
{
- // Nicely format the output stream, so we may print it cleanly
- source.WriteByte((byte)'\n');
+ source.WriteByte((byte)'\n'); // put the next statement on a newline so the exported list of commands is clean
}
result();
if (((Statements)statementType).Equals(Statements.exit))
@@ -247,15 +242,17 @@ namespace Assignment_1
}
else if (source.Position != lastLinePos)
{
- // 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
+ // If the semicolon is missing *once*, assume we need more data.
lastLinePos = source.Position;
cont = true;
- source.WriteByte((byte)' ');
Console.Write(">");
}
else
{
- throw new ParserException("expected a semi-colon", 0, source.Position);
+ if ((Statements)statementType != Statements.h)
+ {
+ throw new ParserException("expected a semi-colon", 0, source.Position);
+ }
}
}
else
@@ -289,15 +286,11 @@ namespace Assignment_1
}
}
- #region Function Handling
///
/// Checks if the next expression in the source meets the requirements of being a key,
- /// and optionally verify that key exists.
- /// Also contracts the key is not reserved or constant
+ /// and optionally verify that key exists. Acoids overidding the value of constants, and
+ /// ensures all characters are valid
///
- ///
- ///
- ///
private string ValidateKey(Stream source, bool checkExist)
{
long keyEndPos = FindIdentifier(source, out string key);
@@ -315,7 +308,7 @@ namespace Assignment_1
}
else
{
- int indx = Array.FindIndex(key.ToCharArray(), (c) => (c > 122 || c > 90 && c < 97 && c != '_' || c > 57 && c < 65 || c < 48)); // If the overall result is good, move until one isn't
+ int indx = Array.FindIndex(key.ToCharArray(), (c) => (c > 122 || c > 90 && c < 97 && c != '_' || c > 57 && c < 65 || c < 48));
if (indx > -1)
{
throw new ParserException(string.Format("Character \'{0}\' is not valid for an identifier",key[indx]), 0, keyEndPos-key.Length + indx);
@@ -328,8 +321,6 @@ namespace Assignment_1
///
/// Checks if the next expression meets the requirements of being a value
///
- ///
- ///
private string ValidateValue(Stream source)
{
long valuePos = FindExpression(source, out string value);
@@ -345,11 +336,9 @@ namespace Assignment_1
}
///
- /// Handles the 'append x y [ + z];' case &
+ /// Handles the 'append x y [ + z];' case
/// And the 'set x y [ + z];' case
///
- ///
- /// An Action that will add the key to the dictionary
Action AppendSet(Stream source, bool appendMode = true)
{
string key = ValidateKey(source, appendMode);
@@ -370,6 +359,7 @@ namespace Assignment_1
}
}
}
+
///
/// Creates and prints a nicely formatted table of all values
///
@@ -413,6 +403,9 @@ namespace Assignment_1
consoleOutput.Append(string.Format("└" + new string('─', keyWidth) + "┴" + new string('─', valueWidth) + "┴" + new string('─', flagWidth) + "┘\n"));
return () => Console.WriteLine(consoleOutput.ToString());
}
+ ///
+ /// Exit Application logic
+ ///
Action Exit(Stream source, long initialStreamLength, bool isDynamicInput=false)
{
void exitAction()
@@ -445,6 +438,14 @@ namespace Assignment_1
}
return exitAction;
}
+
+ ///
+ /// Prints the expression to the console:
+ /// 0: print the value
+ /// 1: print the length
+ /// 2: print the word count
+ /// 3: print the words in the value
+ ///
Action Print(Stream source, int mode = 0)
{
StringBuilder outputString = new StringBuilder();
@@ -478,7 +479,9 @@ namespace Assignment_1
}
return () => Console.WriteLine(outputString.ToString());
}
-
+ ///
+ /// Reverses the word-order of the symbol (in-place).
+ ///
Action Reverse(Stream source)
{
string key = ValidateKey(source, true);
@@ -495,14 +498,10 @@ namespace Assignment_1
}
///
- /// Writes the debug info to the screen in the form:
- /// line read from stream (lineStart) to line end
+ /// Writes the debug info to the screen in the form:
+ /// line read from stream (lineStart) to line end
/// <whitespace@caratPos> ^ <errorMessage>
///
- ///
- ///
- ///
- ///
static void WriteDebugLine(long lineStart, long caratPos, string errorMessage, Stream source)
{
source.Position = lineStart;
@@ -513,18 +512,13 @@ namespace Assignment_1
source.Position = lineStart;
source.SetLength(source.Position);
}
- #endregion
- #region Data Handling
+
///
/// Parses & evaluates the expression from the stream, moving the stream to the end of the last value
///
- ///
- ///
- ///
long FindExpression(Stream s, out string expression)
{
string result = "";
- // 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))
{
@@ -550,17 +544,14 @@ namespace Assignment_1
{
throw new ParserException("Append operator not set", 0, s.Position);
}
-
}
}
expression = result;
return s.Position;
}
///
- /// Checks ahead to see if the next non-whitespace character is the EoS indicator (';')
+ /// Checks ahead to see if the next non-whitespace character is the EoS indicator
///
- ///
- ///
/// true if the next char is , else false
static bool IsNextEoS(Stream s, char EoSChar = ';')
{
@@ -576,11 +567,8 @@ namespace Assignment_1
}
///
- /// Finds the next value in the stream
+ /// Finds the next value expression in the stream
///
- ///
- ///
- ///
long FindValue(Stream s, out string returnedValue)
{
SkipWhitespace(s);
@@ -593,16 +581,12 @@ namespace Assignment_1
else
{
long t = FindExistingIdentifier(s, out string keyValue);
- // Set the key value to result + this read string
- //keyValue = result + keyValue;
-
if (!Symbols.ContainsKey(keyValue))
{
throw new ParserException("Could not find key: " + keyValue, 0, s.Position);
}
returnedValue = Symbols[keyValue].Item1;
return t;
-
}
}
@@ -621,7 +605,6 @@ namespace Assignment_1
wordEnd--;
s.Position--;
}
- // Lookup the value in the symbol table
returnedKey = identifier;
return wordEnd;
}
@@ -629,9 +612,6 @@ namespace Assignment_1
///
/// Finds the end of the complete literal definition, returning the stream to the original position
///
- ///
- ///
- ///
static long FindLiteral(Stream s, out string returnedLiteral)
{
long pos = s.Position;
@@ -642,14 +622,16 @@ namespace Assignment_1
{
if (c == '\"')
{
- long pos = s.Position--;
- if (ReadChar(s) == '\\')
+ s.Position--;
+ if (PreviousChar(s) == '\\')
{
// TODO: handle the \\ escape
+ s.Position++;
return false;
}
else
{
+ s.Position++;
return true;
}
}
@@ -657,7 +639,7 @@ namespace Assignment_1
}, out string resultLiteral);
if (resultPosition > -1)
{
- returnedLiteral = resultLiteral;
+ returnedLiteral = resultLiteral.Replace("\\\"", "\"");
}
else
{
@@ -666,16 +648,11 @@ namespace Assignment_1
s.Position = pos;
return resultPosition;
}
- #endregion
}
-
- #region HelperFunctions
///
/// Reads the memory stream as a UTF-8 encoded string until the next occurance of '\n' or '\r\n' (consuming, and excluded)
///
- ///
- ///
static string GetNextLine(Stream s)
{
FindNextOccurance(s, '\n', out string nextLine);
@@ -685,26 +662,15 @@ namespace Assignment_1
///
/// Finds the end-boundary of the next word in the stream, and returns the stream to the original position
///
- ///
- ///
- ///
static long FindNextWord(Stream s, out string nextWord)
{
StringBuilder newWord = new StringBuilder();
- // Record our current position
long start = s.Position;
- // Check if the character at the current pos is whitespace, if so, keep advancing until it isn't.
- // NB: Whitespace includes carriage returns and line feeds,
- // so 'set\r\n
- // var
- // "expression";
- // should be valid
char currentChar = ReadChar(s);
while (s.Position < s.Length && char.IsWhiteSpace(currentChar))
{
currentChar = ReadChar(s);
}
- // Add the last read value to the SB
newWord.Append(currentChar);
// Start a second loop, this time checking we're not a whitespace char
while (s.Position < s.Length)
@@ -731,10 +697,6 @@ namespace Assignment_1
///
/// Finds and returns the position of the next occurance of the Func returning true.
///
- ///
- /// A 'predicate'-like Func
- /// Returns the string captured while searching for the next char
- ///
static long FindNextOccurance(Stream s, Func p, out string result)
{
long start = s.Position;
@@ -764,21 +726,16 @@ namespace Assignment_1
}
///
- /// Finds the next position of the character
+ /// Finds the next position of the supplied character
///
- ///
- ///
- /// Captures the string read in searching for the character
- ///
static long FindNextOccurance(Stream s, char c, out string result)
{
return FindNextOccurance(s, (streamChar, s) => streamChar == c, out result);
}
+
///
/// Reads the next UTF-8 encoded character in the stream, and advances the stream by the amount of characters read
///
- ///
- ///
static char ReadChar(Stream s)
{
// As UTF-8 allows codepoints to span multiple bytes, reading a single byte as a character will not always give the expected
@@ -815,11 +772,10 @@ namespace Assignment_1
string converted = Encoding.UTF8.GetString(charBytes);
return converted[0];
}
+
///
/// Reads the next character in the stream, and returns the position to the original position
///
- ///
- ///
static char PeekChar(Stream s)
{
long curr = s.Position;
@@ -831,8 +787,6 @@ namespace Assignment_1
///
/// Reads the previous char
///
- ///
- ///
static char PreviousChar(Stream s)
{
Stack charBytes = new Stack(4);
@@ -855,13 +809,12 @@ namespace Assignment_1
///
/// Skips whitespace characters
///
- ///
static void SkipWhitespace(Stream s)
{
char c = PeekChar(s);
while (s.Position < s.Length && char.IsWhiteSpace(c))
{
- ReadChar(s); // move by the size of that character
+ ReadChar(s);
c = PeekChar(s);
}
}
@@ -889,7 +842,7 @@ namespace Assignment_1
}
return lines;
}
- #endregion
+
public class ParserException : Exception
{
///