Refactored position handling into helper functions, e.g.

PeekChar, ReadChar etc.
This commit is contained in:
Brychan Dempsey 2021-03-15 15:03:36 +13:00
parent 482ecc6468
commit c60d75d5ba

View File

@ -35,6 +35,8 @@ namespace Assignment_1
{ {
'$', '\\', '\"', '\'' '$', '\\', '\"', '\''
}; };
static void Main(string[] args) static void Main(string[] args)
{ {
Console.WriteLine("┌──────────────────────────────────────────┐"); Console.WriteLine("┌──────────────────────────────────────────┐");
@ -211,7 +213,9 @@ namespace Assignment_1
Symbols[key] = new Tuple<string, VariableFlags>(Symbols[key].Item1 + value, Symbols[key].Item2); Symbols[key] = new Tuple<string, VariableFlags>(Symbols[key].Item1 + value, Symbols[key].Item2);
return true; return true;
} }
/// <summary>
/// Creates and prints a list of all defined variables
/// </summary>
void List() void List()
{ {
Console.WriteLine("┌" + new string('─', 15) + "┬" + new string('─', 25) + "┬" + new string('─', 9) + "┐"); Console.WriteLine("┌" + new string('─', 15) + "┬" + new string('─', 25) + "┬" + new string('─', 9) + "┐");
@ -385,36 +389,70 @@ namespace Assignment_1
#region Data Handling #region Data Handling
// Data Handling // Data Handling
/// <summary> /// <summary>
/// Parses the expression from the point in the string /// Parses & evaluates the expression from the stream, moving the stream to the end of the last value
/// </summary> /// </summary>
/// <param name="s"></param> /// <param name="s"></param>
/// <param name="expression"></param> /// <param name="expression"></param>
/// <returns></returns> /// <returns></returns>
long FindExpression(Stream s, out string expression) long FindExpression(Stream s, out string expression)
{ {
// must contain at least one value // Expressions are one or more occurances of a variable name or literal definition.
// To make logical sense, there needs to be an operator between them. Typically, for strings, this is
// the append operator: +
// Variable symbols should be evaluated immediately.
// Start by ensuring we don't try reading past the end of the stream
// Also check for the EoS
string result = "";
while (s.Position < s.Length && !IsNextEoS(s))
{
}
// must contain at least one value, so parse the next word
string result; string result;
long wordEnd = FindValue(s, out result); long wordEnd = FindValue(s, out result);
while (true) int sequenceCount = 0;
// If the word after the word we just parsed is the concatenation operator ('+'),
// then there may be more we can parse afterwards.
// Ensure we aren't at the end of the stream; the next value isn't the EoS, and that we haven't
// parsed two values in a row
while (s.Position < s.Length && !IsNextEoS(s) && sequenceCount < 2)
{ {
string nextWord; if (IsNextEoS(s, '+'))
wordEnd = FindNextWord(s, out nextWord);
if (wordEnd > 0 && nextWord == "+")
{ {
s.Position = wordEnd; // next char is an append; skip
sequenceCount = 0;
s.Position = FindNextWord(s, out _);
} }
else else
{ {
break; sequenceCount++;
string tValue;
s.Position = FindValue(s, out tValue);
result += tValue;
} }
s.Position = wordEnd;
wordEnd = FindNextWord(s, out nextWord);
result += nextWord;
} }
expression = result; expression = result;
return wordEnd; return wordEnd;
} }
/// <summary>
/// Checks ahead to see if the next non-whitespace character is the EoS indicator (';')
/// </summary>
/// <param name="s"></param>
/// <param name="EoSChar"></param>
/// <returns>true if the next char is <paramref name="EoSChar"/>, else false</returns>
static bool IsNextEoS(Stream s, char EoSChar = ';')
{
char readChar = PeekChar(s);
while (readChar != -1 && char.IsWhiteSpace(readChar))
{
readChar = PeekChar(s);
}
if (readChar == EoSChar) return true;
else return false;
}
// Most atomic unit is 'value': // Most atomic unit is 'value':
/// <summary> /// <summary>
@ -477,7 +515,7 @@ namespace Assignment_1
if (c == '\"') if (c == '\"')
{ {
long pos = s.Position--; long pos = s.Position--;
if (GetChar(s) == '\\') if (ReadChar(s) == '\\')
{ {
// TODO: handle the \\ escape // TODO: handle the \\ escape
return false; return false;
@ -503,7 +541,7 @@ namespace Assignment_1
} }
#region HelperFunctions
/// <summary> /// <summary>
/// Reads the memory stream as a UTF-8 encoded string until the next occurance of '\n' or '\r\n' (consuming, and excluded) /// Reads the memory stream as a UTF-8 encoded string until the next occurance of '\n' or '\r\n' (consuming, and excluded)
/// </summary> /// </summary>
@ -515,36 +553,9 @@ namespace Assignment_1
FindNextOccurance(s, '\n', out nextLine); FindNextOccurance(s, '\n', out nextLine);
return nextLine; return nextLine;
} }
/// <summary>
/// Finds the next word in the string
/// </summary>
/// <param name="s"></param>
/// <param name="nextWord"></param>
/// <returns>A value <0 if an error occurred, else the position of the end of the word</returns>
static long FindNextWord(string s, out string nextWord)
{
// remove whitespace from the start
int wordStart = 0;
if (char.IsWhiteSpace(s[0]))
{
for (int i = 0; i < s.Length; i++)
{
if (char.IsWhiteSpace(s[i])) break;
wordStart = i;
}
}
int wordEnd = wordStart;
for (int i = wordEnd; i < s.Length; i++)
{
if (char.IsWhiteSpace(s[i])) break;
wordEnd = i;
}
nextWord = s.Substring(wordStart, wordEnd);
return wordEnd;
}
/// <summary> /// <summary>
/// Finds the end-boundary of the next word in the stream /// Finds the end-boundary of the next word in the stream, and returns the stream to the original position
/// </summary> /// </summary>
/// <param name="s"></param> /// <param name="s"></param>
/// <param name="nextWord"></param> /// <param name="nextWord"></param>
@ -570,17 +581,17 @@ namespace Assignment_1
/// Finds and returns the position of the next occurance of the Func returning true. /// Finds and returns the position of the next occurance of the Func returning true.
/// </summary> /// </summary>
/// <param name="s"></param> /// <param name="s"></param>
/// <param name="p"></param> /// <param name="p">A 'predicate'-like Func</param>
/// <param name="result"></param> /// <param name="result">Returns the string captured while searching for the next char</param>
/// <returns></returns> /// <returns></returns>
static long FindNextOccurance(Stream s, Func<char, Stream, bool> p, out string result) static long FindNextOccurance(Stream s, Func<char, Stream, bool> p, out string result)
{ {
long start = s.Position; long start = s.Position;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
bool charFound = false; bool charFound = false;
while (!charFound) while (s.Position < s.Length && !charFound)
{ {
char nextChar = GetChar(s); char nextChar = ReadChar(s);
if (nextChar == 0) if (nextChar == 0)
{ {
charFound = true; charFound = true;
@ -612,8 +623,12 @@ namespace Assignment_1
{ {
return FindNextOccurance(s, (streamChar, s) => streamChar == c, out result); return FindNextOccurance(s, (streamChar, s) => streamChar == c, out result);
} }
/// <summary>
static char GetChar(Stream s) /// Reads the next UTF-8 encoded character in the stream, and advances the stream by the amount of characters read
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
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 // As UTF-8 allows codepoints to span multiple bytes, reading a single byte as a character will not always give the expected
// value. // value.
@ -649,15 +664,31 @@ namespace Assignment_1
string converted = Encoding.UTF8.GetString(charBytes); string converted = Encoding.UTF8.GetString(charBytes);
return converted[0]; return converted[0];
} }
/// <summary>
/// Reads the next character in the stream, and returns the position to the original position
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
static char PeekChar(Stream s)
{
long curr = s.Position;
char c = ReadChar(s);
s.Position = curr;
return c;
}
/// <summary>
/// Skips whitespace characters
/// </summary>
/// <param name="s"></param>
static void SkipWhitespace(Stream s) static void SkipWhitespace(Stream s)
{ {
int readByte = s.ReadByte(); char c = PeekChar(s);
while(readByte > -1 && char.IsWhiteSpace((char)readByte)) while (s.Position < s.Length && char.IsWhiteSpace(c))
{ {
readByte = s.ReadByte(); s.Position++;
c = PeekChar(s);
} }
s.Position--;
} }
static string CenterString(string source, int totalPadding, char paddingChar=' ') static string CenterString(string source, int totalPadding, char paddingChar=' ')
@ -671,5 +702,6 @@ namespace Assignment_1
string result = string.Format(t, source.Substring(0,leftHalf+1), source.Substring(rightHalf,source.Length-rightHalf)); string result = string.Format(t, source.Substring(0,leftHalf+1), source.Substring(rightHalf,source.Length-rightHalf));
return result; return result;
} }
#endregion
} }
} }