178 lines
6.3 KiB
C#
178 lines
6.3 KiB
C#
|
using System;
|
|||
|
using System.IO;
|
|||
|
using System.Text;
|
|||
|
|
|||
|
namespace Assignment_1
|
|||
|
{
|
|||
|
class Program
|
|||
|
{
|
|||
|
static void Main(string[] args)
|
|||
|
{
|
|||
|
Console.WriteLine("┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓");
|
|||
|
Console.WriteLine("┃ 159.341 2021 Semester 1, Assignment 1 ┃");
|
|||
|
Console.WriteLine("┃ Submitted by Brychan Dempsey, 14299890 ┃");
|
|||
|
Console.WriteLine("┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛");
|
|||
|
MemoryStream sourceStream = new MemoryStream(1024); // Creates a memory stream to retain source while being interpreted.
|
|||
|
Parser parser = new Parser();
|
|||
|
bool dynamicInput = false;
|
|||
|
// 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
|
|||
|
if (Console.IsInputRedirected)
|
|||
|
{
|
|||
|
sourceStream.Write(Encoding.UTF8.GetBytes(Console.In.ReadToEnd()));
|
|||
|
sourceStream.Position = 0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
sourceStream.Write(Encoding.UTF8.GetBytes("{\r\n"));
|
|||
|
sourceStream.Position = 0;
|
|||
|
dynamicInput = true;
|
|||
|
}
|
|||
|
parser.FindProgram(sourceStream, dynamicInput);
|
|||
|
}
|
|||
|
public class Parser
|
|||
|
{
|
|||
|
public int FindProgram(Stream sourceStream, bool dynamicInput = false)
|
|||
|
{
|
|||
|
if (sourceStream.ReadByte() == '{')
|
|||
|
{
|
|||
|
FindStatement();
|
|||
|
}
|
|||
|
else return -1; // Could not find the start of the program
|
|||
|
}
|
|||
|
|
|||
|
void Command_Exit()
|
|||
|
{
|
|||
|
Environment.Exit(0);
|
|||
|
}
|
|||
|
|
|||
|
int FindStatement(Stream s)
|
|||
|
{
|
|||
|
string statement;
|
|||
|
if (dynamicInput)
|
|||
|
{
|
|||
|
Console.Write("Enter a \'Statement\': ");
|
|||
|
statement = Console.ReadLine();
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
statement = GetNextLine(s);
|
|||
|
}
|
|||
|
|
|||
|
int wordBound = 0;
|
|||
|
string nextWord = GetNextWord(statement, out wordBound);
|
|||
|
switch (nextWord)
|
|||
|
{
|
|||
|
case "append":
|
|||
|
FindIdentifier();
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
// Look for further elements
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
int FindIdentifier(Stream s)
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Reads the memory stream as a UTF-8 encoded string until the next occurance of '\n' or '\r\n' (consuming, and excluded)
|
|||
|
/// </summary>
|
|||
|
/// <param name="s"></param>
|
|||
|
/// <returns></returns>
|
|||
|
static string GetNextLine(Stream s)
|
|||
|
{
|
|||
|
long start = s.Position;
|
|||
|
StringBuilder sb = new StringBuilder();
|
|||
|
bool newLineFound = false;
|
|||
|
while (!newLineFound)
|
|||
|
{
|
|||
|
// As UTF-8 allows codepoints to span multiple bytes, reading a single byte as a character will not always give the expected
|
|||
|
// value.
|
|||
|
// Fortunately, the standard ASCII table is 7-bits long. The 8th bit is used to determine the character size
|
|||
|
int readAmount = 0;
|
|||
|
int firstChar = s.ReadByte();
|
|||
|
if ((firstChar >> 3) == 0x1E) // 11110xxx implies a 4-byte length character
|
|||
|
{
|
|||
|
readAmount = 3;
|
|||
|
}
|
|||
|
else if((firstChar >> 4) == 0xE) // 1110xxxx, 3-byte
|
|||
|
{
|
|||
|
readAmount = 2;
|
|||
|
}
|
|||
|
else if ((firstChar >> 5) == 0x6) // 110xxxxx, 2-byte
|
|||
|
{
|
|||
|
readAmount = 1;
|
|||
|
}
|
|||
|
|
|||
|
byte[] charBytes = new byte[readAmount + 1];
|
|||
|
charBytes[0] = (byte)firstChar;
|
|||
|
for (int i = 1; i < readAmount; i++)
|
|||
|
{
|
|||
|
int nextChar = s.ReadByte();
|
|||
|
if (nextChar >> 6 != 2) throw new Exception("Character is not a valid UTF-8 code point!");
|
|||
|
charBytes[i] = (byte)nextChar;
|
|||
|
}
|
|||
|
string converted = Encoding.UTF8.GetString(charBytes);
|
|||
|
if (converted == "\r" || converted == "\n")
|
|||
|
{
|
|||
|
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)
|
|||
|
{
|
|||
|
// 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;
|
|||
|
}
|
|||
|
wordEndPos = wordEnd;
|
|||
|
return s.Substring(wordStart, wordEnd);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|