Fixed invalid characters in the identifier string. (Closes #7)
Some checks failed
continuous-integration/appveyor/branch AppVeyor build failed

This commit is contained in:
Brychan Dempsey 2021-03-29 14:02:25 +13:00
parent 093b4455a6
commit c9b9438519
2 changed files with 43 additions and 52 deletions

View File

@ -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
{
/// <summary>
/// Flags to set object properties.
/// Flags to set symbol properties
/// </summary>
[Flags]
enum VariableFlags
@ -17,27 +20,17 @@ namespace Assignment_1
Empty = 0,
Reserved = 1,
NoPrint = 2,
Static = 4,
Undef = 8
Static = 4
}
/// <summary>
/// Characters that cannot appear in a normal string
/// </summary>
static readonly List<char> ForbiddenChars = new List<char>()
{
'$',
'\\',
'\"',
'\''
};
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,12 +306,17 @@ namespace Assignment_1
{
throw new ParserException("Key not found", 0, source.Position);
}
else
{
if (Symbols.ContainsKey(key) && Symbols[key].Item2.HasFlag(VariableFlags.Reserved))
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
{
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(string.Format("Character \'{0}\' is not valid for an identifier",key[indx]), 0, keyEndPos-key.Length + indx);
}
source.Position = keyEndPos;
}
return key;
@ -377,13 +373,13 @@ namespace Assignment_1
/// <param name="printUnprint">List values normally excluded from printing</param>
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<string> eligibleKeys = new List<string>(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
/// <summary>
/// Parses & evaluates the expression from the stream, moving the stream to the end of the last value
/// </summary>
@ -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
/// <param name="s"></param>
/// <param name="returnedLiteral"></param>
/// <returns></returns>
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

View File

@ -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).