diff --git a/Assignment 1.sln b/Assignment 1.sln new file mode 100644 index 0000000..6a64dba --- /dev/null +++ b/Assignment 1.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31025.194 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Assignment 1", "Assignment 1\Assignment 1.csproj", "{90821384-3BA4-4373-A08C-DA6BC25D688A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {90821384-3BA4-4373-A08C-DA6BC25D688A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {90821384-3BA4-4373-A08C-DA6BC25D688A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {90821384-3BA4-4373-A08C-DA6BC25D688A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {90821384-3BA4-4373-A08C-DA6BC25D688A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E0DB9C36-B3F8-40EA-B829-0EF4C528D43B} + EndGlobalSection +EndGlobal diff --git a/Assignment 1/Assignment 1.csproj b/Assignment 1/Assignment 1.csproj new file mode 100644 index 0000000..76fc6da --- /dev/null +++ b/Assignment 1/Assignment 1.csproj @@ -0,0 +1,9 @@ + + + + Exe + net5.0 + Assignment_1 + + + diff --git a/Assignment 1/Program.cs b/Assignment 1/Program.cs new file mode 100644 index 0000000..31ff4c2 --- /dev/null +++ b/Assignment 1/Program.cs @@ -0,0 +1,177 @@ +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); + } + } +}