From c9b94385197183a6215d263ced71e3078e255c9f Mon Sep 17 00:00:00 2001 From: Brychan Dempsey Date: Mon, 29 Mar 2021 14:02:25 +1300 Subject: [PATCH] Fixed invalid characters in the identifier string. (Closes #7) --- Assignment 1/Program.cs | 91 +++++++++++++++++++---------------------- readme.md | 4 +- 2 files changed, 43 insertions(+), 52 deletions(-) diff --git a/Assignment 1/Program.cs b/Assignment 1/Program.cs index e7e8203..4469f36 100644 --- a/Assignment 1/Program.cs +++ b/Assignment 1/Program.cs @@ -1,4 +1,7 @@ -using System; +/* Dempsey-Jensen, Brychan, 14299890, Assignment 1, 159.341 */ +/* Parses and interprets a simple programming language line-by-line */ + +using System; using System.Collections.Generic; using System.IO; using System.Text; @@ -9,7 +12,7 @@ namespace Assignment_1 class Program { /// - /// Flags to set object properties. + /// Flags to set symbol properties /// [Flags] enum VariableFlags @@ -17,27 +20,17 @@ namespace Assignment_1 Empty = 0, Reserved = 1, NoPrint = 2, - Static = 4, - Undef = 8 + Static = 4 } - /// - /// Characters that cannot appear in a normal string - /// - static readonly List ForbiddenChars = new List() - { - '$', - '\\', - '\"', - '\'' - }; + static readonly int ConsoleWidthLimit = 80; 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("└──────────────────────────────────────────┘"); - // Parse the source from the memory stream + Console.WriteLine(CenterString("┌──────────────────────────────────────────┐", ConsoleWidthLimit)); + Console.WriteLine(CenterString("│ 159.341 2021 Semester 1, Assignment 1 │", ConsoleWidthLimit)); + Console.WriteLine(CenterString("│ Submitted by Brychan Dempsey, 14299890 │", ConsoleWidthLimit)); + Console.WriteLine(CenterString("└──────────────────────────────────────────┘", ConsoleWidthLimit)); + bool loadedFromFile = false; bool exit = false; while (!exit) { @@ -45,33 +38,29 @@ namespace Assignment_1 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) + // Reading from pipes is equivalent to reading user input, but the input is redirected + if (Console.IsInputRedirected || loadedFromFile) { // To simplify reading, we read all input bytes from the piped input to the stream. - // Not the best way to do it; we don't need to keep any data that has already been read. - // Whilst the stream could be copied excluding already parsed data at each input step, this would rely - // on GC to cleanup afterwards + // Not the best way to do it; we don't need to keep any data that has already been read and parsed successfully. sourceStream.Write(Encoding.UTF8.GetBytes(Console.In.ReadToEnd())); - // Dispose will close a piped input, or piped file in further iterations of the program Console.In.Dispose(); + Console.SetIn(new StreamReader(Console.OpenStandardInput())); + Console.OpenStandardInput(); sourceStream.Position = 0; } else { - //sourceStream.Write(Encoding.UTF8.GetBytes("{ \r\n")); sourceStream.Position = 0; dynamicInput = true; } parser.StartParsing(sourceStream, dynamicInput); Console.WriteLine(Environment.NewLine + new string('─', 40)); - // Not strictly required, but could have problems if we try loading a large program immediately after unloading the last - // NB: parser is out-of-scope from now - GC.Collect(); - Console.WriteLine("\nProgram parsing complete."); - if (dynamicInput == false) + Console.WriteLine("\nProgram Parsed Successfully!"); + + if (Console.IsInputRedirected) { - Thread.Sleep(2000); + Thread.Sleep(3000); Environment.Exit(0); } ConsoleKeyInfo ck = new ConsoleKeyInfo(); @@ -90,13 +79,14 @@ namespace Assignment_1 ck = new ConsoleKeyInfo(); while (ck.Key != ConsoleKey.Y && ck.Key != ConsoleKey.N) { - Console.WriteLine("\nWould you like to pipe data from source file? Y/n:"); + Console.WriteLine("\nWould you like to pipe data from a source file? Y/n:"); ck = Console.ReadKey(true); } if (ck.Key == ConsoleKey.N) { // Set the input to standard input stream Console.SetIn(Console.In); + loadedFromFile = false; } else { @@ -107,6 +97,7 @@ namespace Assignment_1 try { Console.SetIn(File.OpenText(sourcePath)); + loadedFromFile = true; } catch (Exception e) { @@ -315,11 +306,16 @@ namespace Assignment_1 { throw new ParserException("Key not found", 0, source.Position); } + else if (Symbols.ContainsKey(key) && Symbols[key].Item2.HasFlag(VariableFlags.Reserved)) + { + throw new ParserException("Cannot assign a value to a reserved constant", 0, keyEndPos - (key.Length + 1)); + } else { - if (Symbols.ContainsKey(key) && Symbols[key].Item2.HasFlag(VariableFlags.Reserved)) + int indx = Array.FindIndex(key.ToCharArray(), (c) => (c > 122 || c > 90 && c < 97 && c != '_' || c > 57 && c < 65 || c < 48)); // If the overall result is good, move until one isn't + if (indx > -1) { - throw new ParserException("Cannot assign a value to a reserved constant", 0, keyEndPos - (key.Length + 1)); + throw new ParserException(string.Format("Character \'{0}\' is not valid for an identifier",key[indx]), 0, keyEndPos-key.Length + indx); } source.Position = keyEndPos; } @@ -377,13 +373,13 @@ namespace Assignment_1 /// List values normally excluded from printing Action List(bool printUnprint = false) { - int keyWidth = 21; - int valueWidth = 50; - int flagWidth = 9; + int flagWidth = Math.Max(Enum.GetNames(typeof(VariableFlags)).Length, "Flags".Length); + int keyWidth = (int)((ConsoleWidthLimit - flagWidth) * 0.2); // 20% - flag width + int valueWidth = (int)((ConsoleWidthLimit - flagWidth) * 0.8); // 80% - flag width + StringBuilder consoleOutput = new StringBuilder(); consoleOutput.Append(string.Format("┌" + new string('─', keyWidth) + "┬" + new string('─', valueWidth) + "┬" + new string('─', flagWidth) + "┐\n")); consoleOutput.Append(string.Format("│{0}│{1}│{2}│\n", CenterString("Symbol", keyWidth), CenterString("Value", valueWidth), CenterString("Flags", flagWidth))); - // Figure out how many symbols are eligible for printing List eligibleKeys = new List(Symbols.Count); foreach (var item in Symbols.Keys) { @@ -392,7 +388,6 @@ namespace Assignment_1 eligibleKeys.Add(item); } } - // Control printing based on how many keys are available if (eligibleKeys.Count > 0) { consoleOutput.Append(string.Format("├" + new string('─', keyWidth) + "┼" + new string('─', valueWidth) + "┼" + new string('─', flagWidth) + "┤\n")); @@ -404,7 +399,7 @@ namespace Assignment_1 for (int j = 0; j < (keyLines.Count > valueLines.Count ? keyLines.Count : valueLines.Count); j++) { - consoleOutput.Append(string.Format(entryFormat, j >= keyLines.Count ? "" : keyLines[j], j >= valueLines.Count ? "" : valueLines[j], j == 0 ? Convert.ToString((byte)Symbols[eligibleKeys[i]].Item2, 2).PadLeft(8, '0'): "")); + consoleOutput.Append(string.Format(entryFormat, j >= keyLines.Count ? "" : keyLines[j], j >= valueLines.Count ? "" : valueLines[j], j == 0 ? Convert.ToString((byte)Symbols[eligibleKeys[i]].Item2, 2).PadLeft(flagWidth, '0'): "")); } if (i + 1 < eligibleKeys.Count) { @@ -413,12 +408,11 @@ namespace Assignment_1 } } consoleOutput.Append(string.Format("└" + new string('─', keyWidth) + "┴" + new string('─', valueWidth) + "┴" + new string('─', flagWidth) + "┘\n")); - return () => Console.WriteLine(consoleOutput.ToString()); } Action Exit(Stream source, long initialStreamLength, bool isDynamicInput=false) { - Action exitAction = () => + void exitAction() { if (source.Length != initialStreamLength && isDynamicInput) { @@ -436,8 +430,6 @@ namespace Assignment_1 if (path != "") { path = Path.Combine(Environment.CurrentDirectory, path); - // insert the final closing bracket - //source.WriteByte((byte)'}'); source.Position = 0; using (FileStream fs = File.OpenWrite(path)) { @@ -447,7 +439,7 @@ namespace Assignment_1 } } } - }; + } return exitAction; } Action Print(Stream source, int mode = 0) @@ -520,7 +512,6 @@ namespace Assignment_1 } #endregion #region Data Handling - // Data Handling /// /// Parses & evaluates the expression from the stream, moving the stream to the end of the last value /// @@ -612,12 +603,12 @@ namespace Assignment_1 } } - long FindIdentifier(Stream s, out string returnedKey) + static long FindIdentifier(Stream s, out string returnedKey) { long wordEnd = FindNextWord(s, out returnedKey); return wordEnd; } - long FindExistingIdentifier(Stream s, out string returnedKey) + static long FindExistingIdentifier(Stream s, out string returnedKey) { long wordEnd = FindNextWord(s, out string identifier); if (identifier.Length > 1 && identifier.EndsWith(';')) @@ -638,7 +629,7 @@ namespace Assignment_1 /// /// /// - long FindLiteral(Stream s, out string returnedLiteral) + static long FindLiteral(Stream s, out string returnedLiteral) { long pos = s.Position; // Is a literal. Now we must parse until we find the end of the literal diff --git a/readme.md b/readme.md index e98c1cb..e232276 100644 --- a/readme.md +++ b/readme.md @@ -51,5 +51,5 @@ Would output: ## Additional Parameters | Behaviours * The option `list;` may be augmented with the parameter `all` (i.e. `list all;`), which will additionally list the constant/global symbols. * It is possible to save the command list on `exit;`; this is prompted and guided. -* The program instances its operation; at `exit;` you will have the option to load a new file or write a new program -* A quick help option is available by using the command `h` (semi-colon not required) +* The program instances its operation; at `exit;` you will have the option to load a new file or write a new program. +* A quick help option is available by using the command `h` (semi-colon not required).