Refactored position handling into helper functions, e.g.
PeekChar, ReadChar etc.
This commit is contained in:
parent
482ecc6468
commit
c60d75d5ba
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user