Restructured most of the project, clarified implementations

This commit is contained in:
Brychan Dempsey 2021-03-11 16:18:05 +13:00
parent 6d73e88105
commit 3905b46e75

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
@ -6,6 +7,27 @@ namespace Assignment_1
{ {
class Program class Program
{ {
enum VariableFlags
{
Empty = 0,
Reserved = 1
}
/// <summary>
/// This captures the end-point of each part of an expression (in the stream), to validate the syntax
/// Optionally also captures the parsed string for each expression.
///
/// For this program, a word is considered to be any non-whitespace value bounded by whitespace or the array boundary.
///
/// </summary>
static Dictionary<string, Tuple<string, VariableFlags>> Symbols = new Dictionary<string, Tuple<string, VariableFlags>>
{
{ "SPACE", new Tuple<string, VariableFlags>(" ", VariableFlags.Reserved) },
{ "TAB", new Tuple<string, VariableFlags>("\t", VariableFlags.Reserved) },
{ "NEWLINE", new Tuple<string, VariableFlags>("\n", VariableFlags.Reserved) },
{ "CARRIAGE_RETURN", new Tuple<string, VariableFlags>("\r", VariableFlags.Reserved) }
};
static void Main(string[] args) static void Main(string[] args)
{ {
Console.WriteLine("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"); Console.WriteLine("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓");
@ -13,6 +35,9 @@ namespace Assignment_1
Console.WriteLine("┃ Submitted by Brychan Dempsey, 14299890 ┃"); Console.WriteLine("┃ Submitted by Brychan Dempsey, 14299890 ┃");
Console.WriteLine("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"); Console.WriteLine("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛");
MemoryStream sourceStream = new MemoryStream(1024); // Creates a memory stream to retain source while being interpreted. MemoryStream sourceStream = new MemoryStream(1024); // Creates a memory stream to retain source while being interpreted.
Parser parser = new Parser(); Parser parser = new Parser();
bool dynamicInput = false; bool dynamicInput = false;
// 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
@ -28,76 +53,181 @@ namespace Assignment_1
sourceStream.Position = 0; sourceStream.Position = 0;
dynamicInput = true; dynamicInput = true;
} }
parser.FindProgram(sourceStream, dynamicInput); parser.StartParsing(sourceStream, dynamicInput);
} }
public class Parser public class Parser
{ {
public int FindProgram(Stream sourceStream, bool dynamicInput = false) public enum statements
{ {
if (sourceStream.ReadByte() == '{') exit,
{ append,
FindStatement(); list,
print,
printlength,
printwords,
printwordcount,
set,
reverse
} }
else return -1; // Could not find the start of the program public void StartParsing(Stream source, bool dynamicInput = false)
{
} }
void Command_Exit() #region Function Handling
bool Append(Stream source)
{ {
Environment.Exit(0); string key;
} long advance = FindIdentifier(source, out key);
if (advance < 0)
int FindStatement(Stream s)
{ {
string statement; // Error on finding object
if (dynamicInput) return false;
{
Console.Write("Enter a \'Statement\': ");
statement = Console.ReadLine();
} }
else else
{ {
statement = GetNextLine(s); source.Position = advance;
} }
string value;
int wordBound = 0; advance = FindValue(source, out value);
string nextWord = GetNextWord(statement, out wordBound); if (advance < 0)
switch (nextWord)
{ {
case "append": // Error on parsing value
FindIdentifier(); return false;
FindExpression();
break;
case "list":
break;
case "exit":
Environment.Exit(0);
break;
case "print":
break;
case "printlength":
break;
case "printwords":
break;
case "printwordcount":
break;
case "set":
break;
case "reverse":
break;
default:
break;
} }
else
// Look for further elements
return 1;
}
int FindIdentifier(Stream s)
{ {
source.Position = advance;
}
string eol;
FindNextWord(source, out eol);
if (eol[0] != ';')
{
// Expected end-of-statement/end-of-line (;)
return false;
}
if (Symbols[key].Item2 == VariableFlags.Reserved)
{
// Can't assign to reserved items
return false;
}
Symbols[key] = new Tuple<string, VariableFlags>(Symbols[key].Item1 + value, Symbols[key].Item2);
return true;
}
#endregion
#region Data Handling
// Data Handling
/// <summary>
/// Parses the expression from the point in the string
/// </summary>
/// <param name="s"></param>
/// <param name="expression"></param>
/// <returns></returns>
long FindExpression(Stream s, out string expression)
{
// must contain at least one value
string result;
long wordEnd = FindValue(s, out result);
while (true)
{
string nextWord;
wordEnd = FindNextWord(s, out nextWord);
if (wordEnd > 0 && nextWord == "+")
{
s.Position = wordEnd;
}
else
{
break;
}
s.Position = wordEnd;
wordEnd = FindNextWord(s, out nextWord);
result += nextWord;
}
expression = result;
return wordEnd;
}
// Most atomic unit is 'value':
/// <summary>
/// Finds the next value in the stream
/// </summary>
/// <param name="s"></param>
/// <param name="returnedValue"></param>
/// <returns></returns>
long FindValue(Stream s, out string returnedValue)
{
SkipWhitespace(s);
int result = s.ReadByte();
if (result == '\"')
{
return FindLiteral(s, out returnedValue);
}
else
{
string keyValue;
long t = FindIdentifier(s, out keyValue);
returnedValue = Symbols[keyValue].Item1;
return t;
} }
} }
long FindIdentifier(Stream s, out string returnedKey)
{
string identifier;
long wordEnd = FindNextWord(s, out identifier);
// Lookup the value in the symbol table
try
{
returnedKey = Symbols[identifier].Item1;
}
catch (KeyNotFoundException e)
{
Console.WriteLine("Could not find a defined variable with the name {0}", identifier);
Console.Error.WriteLine(e);
returnedKey = "";
return -1;
}
return wordEnd;
}
long FindLiteral(Stream s, out string returnedLiteral)
{
// Is a literal. Now we must parse until we find the end of the literal
string resultLiteral;
long resultPosition = FindNextOccurance(s, (c, s) =>
{
if (c == '\"')
{
long pos = s.Position--;
if (GetChar(s) == '\\')
{
// TODO: handle the \\ escape
return false;
}
else
{
return true;
}
}
return false;
}, out resultLiteral);
if (resultPosition > -1)
{
returnedLiteral = resultLiteral;
}
else
{
returnedLiteral = "";
}
return resultPosition;
}
#endregion
}
/// <summary> /// <summary>
@ -106,11 +236,101 @@ namespace Assignment_1
/// <param name="s"></param> /// <param name="s"></param>
/// <returns></returns> /// <returns></returns>
static string GetNextLine(Stream s) static string GetNextLine(Stream s)
{
string nextLine;
FindNextOccurance(s, '\n', out 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>
/// Finds the end-boundary of the next word in the stream
/// </summary>
/// <param name="s"></param>
/// <param name="nextWord"></param>
/// <returns></returns>
static long FindNextWord(Stream s, out string nextWord)
{
return FindNextOccurance(s, (c, s) => Char.IsWhiteSpace(c), out nextWord);
}
/// <summary>
/// Finds and returns the position of the next occurance of the Func returning true.
/// </summary>
/// <param name="s"></param>
/// <param name="p"></param>
/// <param name="result"></param>
/// <returns></returns>
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 newLineFound = false; bool charFound = false;
while (!newLineFound) while (!charFound)
{
char nextChar = GetChar(s);
if (p(nextChar, s))
{
/*if (c == '\n')
{
s.Position--;
if (s.ReadByte() != '\r') s.Position--;
// Avoid capturing the carriage return
}*/
charFound = true;
}
else
{
sb.Append(nextChar);
}
}
result = sb.ToString();
long newPosition = s.Position;
s.Position = start;
return newPosition;
}
/// <summary>
/// Finds the next position of the character
/// </summary>
/// <param name="s"></param>
/// <param name="c"></param>
/// <param name="result">Captures the string read in searching for the character</param>
/// <returns></returns>
static long FindNextOccurance(Stream s, char c, out string result)
{
return FindNextOccurance(s, (streamChar, s) => streamChar == c, out result);
}
static char GetChar(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.
@ -139,39 +359,16 @@ namespace Assignment_1
charBytes[i] = (byte)nextChar; charBytes[i] = (byte)nextChar;
} }
string converted = Encoding.UTF8.GetString(charBytes); string converted = Encoding.UTF8.GetString(charBytes);
if (converted == "\r" || converted == "\n") return converted[0];
{
if (s.ReadByte() != '\n') s.Position--; // Return the position if the next character isn't a new line
newLineFound = true;
}
else
{
sb.Append(converted);
}
}
return sb.ToString();
} }
static string GetNextWord(string s, out int wordEndPos) static void SkipWhitespace(Stream s)
{ {
// remove whitespace from the start int readByte = s.ReadByte();
int wordStart = 0; while(readByte > -1 && char.IsWhiteSpace((char)readByte))
if (char.IsWhiteSpace(s[0]))
{ {
for (int i = 0; i < s.Length; i++) readByte = s.ReadByte();
{ }
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;
}
wordEndPos = wordEnd;
return s.Substring(wordStart, wordEnd);
} }
} }
} }