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) { } } /// /// 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) { 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); } } }