Compare commits
12 Commits
cpp_varian
...
master
Author | SHA1 | Date | |
---|---|---|---|
eb9d9cc177 | |||
eccecc15a9 | |||
340591e1a1 | |||
a37aa07ede | |||
51920ef69e | |||
c9b9438519 | |||
093b4455a6 | |||
71ec98967d | |||
2c5e3bee92 | |||
303ca42f5f | |||
cdc321768b | |||
1d34d1b6b7 |
5
.gitignore
vendored
5
.gitignore
vendored
@ -360,4 +360,7 @@ MigrationBackup/
|
|||||||
.ionide/
|
.ionide/
|
||||||
|
|
||||||
# Fody - auto-generated XML schema
|
# Fody - auto-generated XML schema
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# Assignment 1 instructions
|
||||||
|
assignment_instructions.txt
|
||||||
|
@ -2,8 +2,12 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||||
<RootNamespace>Assignment_1</RootNamespace>
|
<RootNamespace>Assignment_1</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||||
|
<Exec Command="xcopy "$(ProjectDir)\Examples\*.txt" "$(OutDir)" /Y /I" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
12
Assignment 1/Examples/example1.txt
Normal file
12
Assignment 1/Examples/example1.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
set one "The cat";
|
||||||
|
set two "sat on the mat";
|
||||||
|
set sentence one + SPACE + two;
|
||||||
|
append sentence " by itself.";
|
||||||
|
print sentence;
|
||||||
|
printwords sentence;
|
||||||
|
printwordcount sentence;
|
||||||
|
printlength sentence;
|
||||||
|
reverse sentence;
|
||||||
|
print sentence;
|
||||||
|
list;
|
||||||
|
exit;
|
11
Assignment 1/Examples/example2.txt
Normal file
11
Assignment 1/Examples/example2.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
set var1 "The";
|
||||||
|
set var2 "pesky";
|
||||||
|
set var3 "feline";
|
||||||
|
set var4 "sat,";
|
||||||
|
set var5 "for what is hopefully the last time,";
|
||||||
|
set var6 "on the mat.";
|
||||||
|
set longvar var1 + SPACE;
|
||||||
|
append longvar var2 + SPACE + var3 + SPACE + var4 + SPACE;
|
||||||
|
append longvar var5 + SPACE + var6 + NEWLINE;
|
||||||
|
list;
|
||||||
|
exit;
|
6
Assignment 1/Examples/example3.txt
Normal file
6
Assignment 1/Examples/example3.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
set a1 "To be, or not to be - that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune Or to take arms against a sea of troubles, And by opposing end them. To die - to sleep - No more, and by a sleep to say we end The heartache, and the thousand natural shocks That flesh is heir to. 'Tis a consummation Devoutly to be wish'd. To die - to sleep. To sleep - perchance to dream: ay, there's the rub! For in that sleep of death what dreams may come When we have shuffled off this mortal coil, Must give us pause. There's the respect That makes calamity of so long life. For who would bear the whips and scorns of time, The oppressor's wrong, the proud man's contumely, The pangs of despised love, the law's delay, The insolence of office, and the spurns That patient merit of the unworthy takes, When he himself might his quietus make";
|
||||||
|
print a1;
|
||||||
|
printwords a1;
|
||||||
|
printwordcount a1;
|
||||||
|
printlength a1;
|
||||||
|
exit;
|
49
Assignment 1/Examples/example4.txt
Normal file
49
Assignment 1/Examples/example4.txt
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
set a1 "
|
||||||
|
To be, or not to be - that is the question:
|
||||||
|
Whether 'tis nobler in the mind to suffer
|
||||||
|
The slings and arrows of outrageous fortune
|
||||||
|
Or to take arms against a sea of troubles,
|
||||||
|
And by opposing end them. To die - to sleep -
|
||||||
|
No more; and by a sleep to say we end
|
||||||
|
The heartache, and the thousand natural shocks
|
||||||
|
That flesh is heir to. 'Tis a consummation
|
||||||
|
Devoutly to be wish'd. To die - to sleep.
|
||||||
|
To sleep - perchance to dream: ay, there's the rub!
|
||||||
|
For in that sleep of death what dreams may come
|
||||||
|
When we have shuffled off this mortal coil,
|
||||||
|
Must give us pause. There's the respect
|
||||||
|
That makes calamity of so long life.
|
||||||
|
For who would bear the whips and scorns of time,
|
||||||
|
The oppressor's wrong, the proud man's contumely,
|
||||||
|
The pangs of despised love, the law's delay,
|
||||||
|
The insolence of office, and the spurns
|
||||||
|
That patient merit of the unworthy takes,
|
||||||
|
When he himself might his quietus make" + NEWLINE +
|
||||||
|
"With a bare bodkin?
|
||||||
|
";
|
||||||
|
|
||||||
|
set averylongvariablethathasbeencontrivedjusttobeanuisancetothe341classwhohaveprobablythoughtthattheycouldgetawaywithallocatingshortvariablenamesofashortlengthbutaregoingtobereallydisappointediftheydidbecausethisisnotashortvariableidentifieratall "
|
||||||
|
Who would these fardels bear,
|
||||||
|
To grunt and sweat under a weary life,
|
||||||
|
But that the dread of something after death -
|
||||||
|
The undiscover'd country, from whose bourn
|
||||||
|
No traveller returns - puzzles the will,
|
||||||
|
And makes us rather bear those ills we have
|
||||||
|
Than fly to others that we know not of?
|
||||||
|
Thus conscience does make cowards of us all,
|
||||||
|
And thus the native hue of resolution
|
||||||
|
Is sicklied o'er with the pale cast of thought,
|
||||||
|
And enterprises of great pith and moment
|
||||||
|
With this regard their currents turn awry
|
||||||
|
And lose the name of action.";
|
||||||
|
|
||||||
|
set soliloquy a1 + NEWLINE + averylongvariablethathasbeencontrivedjusttobeanuisancetothe341classwhohaveprobablythoughtthattheycouldgetawaywithallocatingshortvariablenamesofashortlengthbutaregoingtobereallydisappointediftheydidbecausethisisnotashortvariableidentifieratall;
|
||||||
|
|
||||||
|
set dummy "dummy1";
|
||||||
|
|
||||||
|
print soliloquy;
|
||||||
|
printwordcount soliloquy;
|
||||||
|
printwords soliloquy;
|
||||||
|
printlength soliloquy;
|
||||||
|
list;
|
||||||
|
exit;
|
3
Assignment 1/Examples/test1.txt
Normal file
3
Assignment 1/Examples/test1.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
set apple "Apple trees are small.";
|
||||||
|
reverse apple;
|
||||||
|
print apple;
|
@ -1,11 +1,10 @@
|
|||||||
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.Collections.Generic;
|
||||||
using System.Collections;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Assignment_1
|
namespace Assignment_1
|
||||||
@ -13,7 +12,7 @@ namespace Assignment_1
|
|||||||
class Program
|
class Program
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Flags to set object properties.
|
/// Flags to set symbol properties
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Flags]
|
[Flags]
|
||||||
enum VariableFlags
|
enum VariableFlags
|
||||||
@ -21,64 +20,53 @@ namespace Assignment_1
|
|||||||
Empty = 0,
|
Empty = 0,
|
||||||
Reserved = 1,
|
Reserved = 1,
|
||||||
NoPrint = 2,
|
NoPrint = 2,
|
||||||
Static = 4,
|
Static = 4
|
||||||
Undef = 8
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
|
||||||
/// Characters that cannot appear in a normal string
|
// Max display width for tables etc.
|
||||||
/// </summary>
|
static readonly int ConsoleWidthLimit = 80;
|
||||||
static readonly List<char> ForbiddenChars = new()
|
|
||||||
{
|
|
||||||
'$',
|
|
||||||
'\\',
|
|
||||||
'\"',
|
|
||||||
'\''
|
|
||||||
};
|
|
||||||
|
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine("┌──────────────────────────────────────────┐");
|
Console.WriteLine(CenterString("┌──────────────────────────────────────────┐", ConsoleWidthLimit));
|
||||||
Console.WriteLine("│ 159.341 2021 Semester 1, Assignment 1 │");
|
Console.WriteLine(CenterString("│ 159.341 2021 Semester 1, Assignment 1 │", ConsoleWidthLimit));
|
||||||
Console.WriteLine("│ Submitted by Brychan Dempsey, 14299890 │");
|
Console.WriteLine(CenterString("│ Submitted by Brychan Dempsey, 14299890 │", ConsoleWidthLimit));
|
||||||
Console.WriteLine("└──────────────────────────────────────────┘");
|
Console.WriteLine(CenterString("└──────────────────────────────────────────┘", ConsoleWidthLimit));
|
||||||
// Parse the source from the memory stream
|
bool loadedFromFile = false;
|
||||||
bool exit = false;
|
bool exit = false;
|
||||||
while (!exit)
|
while (!exit)
|
||||||
{
|
{
|
||||||
MemoryStream sourceStream = new(1024);
|
MemoryStream sourceStream = new MemoryStream(1024);
|
||||||
Parser parser = new();
|
Parser parser = new Parser();
|
||||||
bool dynamicInput = false;
|
bool dynamicInput = false;
|
||||||
// From https://stackoverflow.com/questions/3453220/how-to-detect-if-console-in-stdin-has-been-redirected
|
// 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
|
// Reading from pipes is equivalent to reading user input, but the input is redirected
|
||||||
if (Console.IsInputRedirected)
|
if (Console.IsInputRedirected || loadedFromFile)
|
||||||
{
|
{
|
||||||
// To simplify reading, we read all input bytes from the piped input to the stream.
|
// 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.
|
// Not the best way to do it; we don't need to keep any data that has already been read and parsed successfully,
|
||||||
// Whilst the stream could be copied excluding already parsed data at each input step, this would rely
|
// and we could avoid copying the supplied stream. But keeps interactivity with the console simple
|
||||||
// on GC to cleanup afterwards
|
|
||||||
sourceStream.Write(Encoding.UTF8.GetBytes(Console.In.ReadToEnd()));
|
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.In.Dispose();
|
||||||
|
Console.SetIn(new StreamReader(Console.OpenStandardInput()));
|
||||||
|
Console.OpenStandardInput();
|
||||||
sourceStream.Position = 0;
|
sourceStream.Position = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//sourceStream.Write(Encoding.UTF8.GetBytes("{ \r\n"));
|
|
||||||
sourceStream.Position = 0;
|
sourceStream.Position = 0;
|
||||||
dynamicInput = true;
|
dynamicInput = true;
|
||||||
}
|
}
|
||||||
parser.StartParsing(sourceStream, dynamicInput);
|
parser.StartParsing(sourceStream, dynamicInput);
|
||||||
Console.WriteLine(Environment.NewLine + new string('─', 40));
|
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
|
Console.WriteLine("\nProgram Parsed Successfully!");
|
||||||
// NB: parser is out-of-scope from now
|
|
||||||
GC.Collect();
|
if (Console.IsInputRedirected)
|
||||||
Console.WriteLine("\nProgram parsing complete.");
|
|
||||||
if (dynamicInput == false)
|
|
||||||
{
|
{
|
||||||
Thread.Sleep(2000);
|
Thread.Sleep(3000);
|
||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
}
|
}
|
||||||
ConsoleKeyInfo ck = new();
|
ConsoleKeyInfo ck = new ConsoleKeyInfo();
|
||||||
while (ck.Key != ConsoleKey.Y && ck.Key != ConsoleKey.N)
|
while (ck.Key != ConsoleKey.Y && ck.Key != ConsoleKey.N)
|
||||||
{
|
{
|
||||||
Console.WriteLine("\nWould you like to parse another program? Y/n:");
|
Console.WriteLine("\nWould you like to parse another program? Y/n:");
|
||||||
@ -90,17 +78,17 @@ namespace Assignment_1
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Need the logic to prep the next source stream
|
ck = new ConsoleKeyInfo();
|
||||||
ck = new();
|
|
||||||
while (ck.Key != ConsoleKey.Y && ck.Key != ConsoleKey.N)
|
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);
|
ck = Console.ReadKey(true);
|
||||||
}
|
}
|
||||||
if (ck.Key == ConsoleKey.N)
|
if (ck.Key == ConsoleKey.N)
|
||||||
{
|
{
|
||||||
// Set the input to standard input stream
|
// Set the input to standard input stream
|
||||||
Console.SetIn(Console.In);
|
Console.SetIn(Console.In);
|
||||||
|
loadedFromFile = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -111,6 +99,7 @@ namespace Assignment_1
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Console.SetIn(File.OpenText(sourcePath));
|
Console.SetIn(File.OpenText(sourcePath));
|
||||||
|
loadedFromFile = true;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@ -124,7 +113,7 @@ namespace Assignment_1
|
|||||||
|
|
||||||
public class Parser
|
public class Parser
|
||||||
{
|
{
|
||||||
Dictionary<string, Tuple<string, VariableFlags>> Symbols = new()
|
Dictionary<string, Tuple<string, VariableFlags>> Symbols = new Dictionary<string, Tuple<string, VariableFlags>>()
|
||||||
{
|
{
|
||||||
{ "SPACE", new Tuple<string, VariableFlags>(" ", VariableFlags.Reserved | VariableFlags.NoPrint) },
|
{ "SPACE", new Tuple<string, VariableFlags>(" ", VariableFlags.Reserved | VariableFlags.NoPrint) },
|
||||||
{ "TAB", new Tuple<string, VariableFlags>("\t", VariableFlags.Reserved | VariableFlags.NoPrint) },
|
{ "TAB", new Tuple<string, VariableFlags>("\t", VariableFlags.Reserved | VariableFlags.NoPrint) },
|
||||||
@ -144,7 +133,6 @@ namespace Assignment_1
|
|||||||
set,
|
set,
|
||||||
reverse,
|
reverse,
|
||||||
h,
|
h,
|
||||||
writeout
|
|
||||||
}
|
}
|
||||||
public void StartParsing(Stream source, bool dynamicInput = false)
|
public void StartParsing(Stream source, bool dynamicInput = false)
|
||||||
{
|
{
|
||||||
@ -152,9 +140,10 @@ namespace Assignment_1
|
|||||||
long lastLinePos = 0;
|
long lastLinePos = 0;
|
||||||
long initPos = 0;
|
long initPos = 0;
|
||||||
bool cont = false;
|
bool cont = false;
|
||||||
|
bool isLineFinished = true;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (dynamicInput)
|
if (dynamicInput && isLineFinished)
|
||||||
{
|
{
|
||||||
lastLinePos = source.Position;
|
lastLinePos = source.Position;
|
||||||
if (!cont)
|
if (!cont)
|
||||||
@ -167,8 +156,6 @@ namespace Assignment_1
|
|||||||
source.Position = pos;
|
source.Position = pos;
|
||||||
|
|
||||||
}
|
}
|
||||||
// parse the statement or list of statements;
|
|
||||||
// This is done by reading the next word
|
|
||||||
if (!cont)
|
if (!cont)
|
||||||
{
|
{
|
||||||
initPos = source.Position;
|
initPos = source.Position;
|
||||||
@ -225,34 +212,27 @@ namespace Assignment_1
|
|||||||
case Statements.reverse:
|
case Statements.reverse:
|
||||||
result = Reverse(source);
|
result = Reverse(source);
|
||||||
break;
|
break;
|
||||||
// These are additional helper functions. Thier input gets excluded from the MemoryStream
|
|
||||||
case Statements.h:
|
case Statements.h:
|
||||||
Console.WriteLine("Commands are: ");
|
Console.WriteLine("Commands are: ");
|
||||||
foreach (var item in Enum.GetValues(typeof(Statements)))
|
foreach (var item in Enum.GetValues(typeof(Statements)))
|
||||||
{
|
{
|
||||||
Console.WriteLine("\t{0}", ((Statements)item).ToString());
|
Console.WriteLine("\t{0}", ((Statements)item).ToString());
|
||||||
}
|
}
|
||||||
// Ignore these as actual commands
|
// Stream ignores this command
|
||||||
source.Position = initPos;
|
source.Position = initPos;
|
||||||
source.SetLength(initPos);
|
source.SetLength(initPos);
|
||||||
break;
|
break;
|
||||||
/*case Statements.writeout:
|
|
||||||
// Writes the full command history to the stream.
|
|
||||||
Console.WriteLine("Writing input commands to {0}...");
|
|
||||||
source.Position = initPos;
|
|
||||||
source.SetLength(initPos);
|
|
||||||
break;*/
|
|
||||||
}
|
}
|
||||||
// Do a check semicolons etc
|
|
||||||
if (IsNextEoS(source))
|
if (IsNextEoS(source))
|
||||||
{
|
{
|
||||||
// Increment the source pos past the semi-colon
|
|
||||||
cont = false;
|
cont = false;
|
||||||
source.Position++;
|
source.Position++;
|
||||||
if (dynamicInput)
|
SkipWhitespace(source);
|
||||||
|
if (source.Position == source.Length) isLineFinished = true;
|
||||||
|
else isLineFinished = false;
|
||||||
|
if (dynamicInput && isLineFinished)
|
||||||
{
|
{
|
||||||
// Nicely format the output stream, so we may print it cleanly
|
source.WriteByte((byte)'\n'); // put the next statement on a newline so the exported list of commands is clean
|
||||||
source.WriteByte((byte)'\n');
|
|
||||||
}
|
}
|
||||||
result();
|
result();
|
||||||
if (((Statements)statementType).Equals(Statements.exit))
|
if (((Statements)statementType).Equals(Statements.exit))
|
||||||
@ -260,17 +240,19 @@ namespace Assignment_1
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (source.Position != lastLinePos)// - 1)
|
else if (source.Position != lastLinePos)
|
||||||
{
|
{
|
||||||
// In the case that we expect some more data, we must keep tabs of our current line, and keep accumulating data until we're finished
|
// If the semicolon is missing *once*, assume we need more data.
|
||||||
lastLinePos = source.Position;
|
lastLinePos = source.Position;
|
||||||
cont = true;
|
cont = true;
|
||||||
source.WriteByte((byte)' ');
|
|
||||||
Console.Write(">");
|
Console.Write(">");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new ParserException("expected a semi-colon", 0, source.Position);
|
if ((Statements)statementType != Statements.h)
|
||||||
|
{
|
||||||
|
throw new ParserException("expected a semi-colon", 0, source.Position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -278,11 +260,12 @@ namespace Assignment_1
|
|||||||
throw new ParserException("Failed parsing statement", 0, source.Position);
|
throw new ParserException("Failed parsing statement", 0, source.Position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Throwing a parserexception will return us to this point immediately. From here, the line is automatically restored,
|
// Throwing a ParserException will return us to this point immediately. From here, the line is automatically restored,
|
||||||
// and the excepion printed to the console window.
|
// and the excepion printed to the console window.
|
||||||
// This means that each function does not need to keep track of our current position in the stream
|
// This means that each function does not need to keep track of our current position in the stream
|
||||||
catch (ParserException e)
|
catch (ParserException e)
|
||||||
{
|
{
|
||||||
|
isLineFinished = true;
|
||||||
if (e.Importance > 3)
|
if (e.Importance > 3)
|
||||||
{
|
{
|
||||||
throw new ApplicationException("A critical error occurred.");
|
throw new ApplicationException("A critical error occurred.");
|
||||||
@ -293,9 +276,7 @@ namespace Assignment_1
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Console.WriteLine(e.LinePosition + ": " + e.Message);
|
WriteDebugLine(0, 0, e.Message, source);
|
||||||
source.Position = initPos;
|
|
||||||
source.SetLength(initPos);
|
|
||||||
}
|
}
|
||||||
if (!dynamicInput)
|
if (!dynamicInput)
|
||||||
{
|
{
|
||||||
@ -305,15 +286,11 @@ namespace Assignment_1
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Function Handling
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the next expression in the source meets the requirements of being a key,
|
/// Checks if the next expression in the source meets the requirements of being a key,
|
||||||
/// and optionally verify that key exists.
|
/// and optionally verify that key exists. Acoids overidding the value of constants, and
|
||||||
/// Also contracts the key is not reserved or constant
|
/// ensures all characters are valid
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source"></param>
|
|
||||||
/// <param name="checkExist"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private string ValidateKey(Stream source, bool checkExist)
|
private string ValidateKey(Stream source, bool checkExist)
|
||||||
{
|
{
|
||||||
long keyEndPos = FindIdentifier(source, out string key);
|
long keyEndPos = FindIdentifier(source, out string key);
|
||||||
@ -325,11 +302,16 @@ namespace Assignment_1
|
|||||||
{
|
{
|
||||||
throw new ParserException("Key not found", 0, source.Position);
|
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
|
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 (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;
|
source.Position = keyEndPos;
|
||||||
}
|
}
|
||||||
@ -339,8 +321,6 @@ namespace Assignment_1
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the next expression meets the requirements of being a value
|
/// Checks if the next expression meets the requirements of being a value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private string ValidateValue(Stream source)
|
private string ValidateValue(Stream source)
|
||||||
{
|
{
|
||||||
long valuePos = FindExpression(source, out string value);
|
long valuePos = FindExpression(source, out string value);
|
||||||
@ -356,11 +336,9 @@ namespace Assignment_1
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles the 'append x y [ + z];' case &
|
/// Handles the 'append x y [ + z];' case <br />
|
||||||
/// And the 'set x y [ + z];' case
|
/// And the 'set x y [ + z];' case
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source"></param>
|
|
||||||
/// <returns>An Action that will add the key to the dictionary</returns>
|
|
||||||
Action AppendSet(Stream source, bool appendMode = true)
|
Action AppendSet(Stream source, bool appendMode = true)
|
||||||
{
|
{
|
||||||
string key = ValidateKey(source, appendMode);
|
string key = ValidateKey(source, appendMode);
|
||||||
@ -381,20 +359,21 @@ namespace Assignment_1
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates and prints a nicely formatted table of all values
|
/// Creates and prints a nicely formatted table of all values
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="printUnprint">List values normally excluded from printing</param>
|
/// <param name="printUnprint">List values normally excluded from printing</param>
|
||||||
Action List(bool printUnprint = false)
|
Action List(bool printUnprint = false)
|
||||||
{
|
{
|
||||||
int keyWidth = 21;
|
int flagWidth = Math.Max(Enum.GetNames(typeof(VariableFlags)).Length, "Flags".Length);
|
||||||
int valueWidth = 50;
|
int keyWidth = (int)((ConsoleWidthLimit - flagWidth) * 0.2); // 20% - flag width
|
||||||
int flagWidth = 9;
|
int valueWidth = (int)((ConsoleWidthLimit - flagWidth) * 0.8); // 80% - flag width
|
||||||
StringBuilder consoleOutput = new();
|
|
||||||
|
StringBuilder consoleOutput = new StringBuilder();
|
||||||
consoleOutput.Append(string.Format("┌" + new string('─', keyWidth) + "┬" + new string('─', valueWidth) + "┬" + new string('─', flagWidth) + "┐\n"));
|
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)));
|
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);
|
||||||
List<string> eligibleKeys = new(Symbols.Count);
|
|
||||||
foreach (var item in Symbols.Keys)
|
foreach (var item in Symbols.Keys)
|
||||||
{
|
{
|
||||||
if (!Symbols[item].Item2.HasFlag(VariableFlags.NoPrint) || (Symbols[item].Item2.HasFlag(VariableFlags.NoPrint) && printUnprint))
|
if (!Symbols[item].Item2.HasFlag(VariableFlags.NoPrint) || (Symbols[item].Item2.HasFlag(VariableFlags.NoPrint) && printUnprint))
|
||||||
@ -402,7 +381,6 @@ namespace Assignment_1
|
|||||||
eligibleKeys.Add(item);
|
eligibleKeys.Add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Control printing based on how many keys are available
|
|
||||||
if (eligibleKeys.Count > 0)
|
if (eligibleKeys.Count > 0)
|
||||||
{
|
{
|
||||||
consoleOutput.Append(string.Format("├" + new string('─', keyWidth) + "┼" + new string('─', valueWidth) + "┼" + new string('─', flagWidth) + "┤\n"));
|
consoleOutput.Append(string.Format("├" + new string('─', keyWidth) + "┼" + new string('─', valueWidth) + "┼" + new string('─', flagWidth) + "┤\n"));
|
||||||
@ -414,7 +392,7 @@ namespace Assignment_1
|
|||||||
|
|
||||||
for (int j = 0; j < (keyLines.Count > valueLines.Count ? keyLines.Count : valueLines.Count); j++)
|
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)
|
if (i + 1 < eligibleKeys.Count)
|
||||||
{
|
{
|
||||||
@ -423,12 +401,14 @@ namespace Assignment_1
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
consoleOutput.Append(string.Format("└" + new string('─', keyWidth) + "┴" + new string('─', valueWidth) + "┴" + new string('─', flagWidth) + "┘\n"));
|
consoleOutput.Append(string.Format("└" + new string('─', keyWidth) + "┴" + new string('─', valueWidth) + "┴" + new string('─', flagWidth) + "┘\n"));
|
||||||
|
|
||||||
return () => Console.WriteLine(consoleOutput.ToString());
|
return () => Console.WriteLine(consoleOutput.ToString());
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Exit Application logic
|
||||||
|
/// </summary>
|
||||||
Action Exit(Stream source, long initialStreamLength, bool isDynamicInput=false)
|
Action Exit(Stream source, long initialStreamLength, bool isDynamicInput=false)
|
||||||
{
|
{
|
||||||
Action exitAction = () =>
|
void exitAction()
|
||||||
{
|
{
|
||||||
if (source.Length != initialStreamLength && isDynamicInput)
|
if (source.Length != initialStreamLength && isDynamicInput)
|
||||||
{
|
{
|
||||||
@ -446,8 +426,6 @@ namespace Assignment_1
|
|||||||
if (path != "")
|
if (path != "")
|
||||||
{
|
{
|
||||||
path = Path.Combine(Environment.CurrentDirectory, path);
|
path = Path.Combine(Environment.CurrentDirectory, path);
|
||||||
// insert the final closing bracket
|
|
||||||
//source.WriteByte((byte)'}');
|
|
||||||
source.Position = 0;
|
source.Position = 0;
|
||||||
using (FileStream fs = File.OpenWrite(path))
|
using (FileStream fs = File.OpenWrite(path))
|
||||||
{
|
{
|
||||||
@ -457,12 +435,20 @@ namespace Assignment_1
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
return exitAction;
|
return exitAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prints the expression to the console:<br />
|
||||||
|
/// 0: print the value <br />
|
||||||
|
/// 1: print the length <br />
|
||||||
|
/// 2: print the word count <br />
|
||||||
|
/// 3: print the words in the value <br />
|
||||||
|
/// </summary>
|
||||||
Action Print(Stream source, int mode = 0)
|
Action Print(Stream source, int mode = 0)
|
||||||
{
|
{
|
||||||
StringBuilder outputString = new();
|
StringBuilder outputString = new StringBuilder();
|
||||||
string expression = ValidateValue(source);
|
string expression = ValidateValue(source);
|
||||||
if (mode == 0)
|
if (mode == 0)
|
||||||
{
|
{
|
||||||
@ -493,13 +479,15 @@ namespace Assignment_1
|
|||||||
}
|
}
|
||||||
return () => Console.WriteLine(outputString.ToString());
|
return () => Console.WriteLine(outputString.ToString());
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Reverses the word-order of the symbol (in-place).
|
||||||
|
/// </summary>
|
||||||
Action Reverse(Stream source)
|
Action Reverse(Stream source)
|
||||||
{
|
{
|
||||||
string key = ValidateKey(source, true);
|
string key = ValidateKey(source, true);
|
||||||
string ToReverse = Symbols[key].Item1;
|
string ToReverse = Symbols[key].Item1;
|
||||||
string[] words = ToReverse.Split(' ');
|
string[] words = ToReverse.Split(' ');
|
||||||
StringBuilder reversed = new();
|
StringBuilder reversed = new StringBuilder();
|
||||||
for (int i = words.Length - 1; i >= 0; i--)
|
for (int i = words.Length - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
reversed.Append(words[i]);
|
reversed.Append(words[i]);
|
||||||
@ -510,14 +498,10 @@ namespace Assignment_1
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes the debug info to the screen in the form:<br/>
|
/// Writes the debug info to the screen in the form: <br/>
|
||||||
/// line read from stream (lineStart) to line end<br/>
|
/// line read from stream (lineStart) to line end <br/>
|
||||||
/// <whitespace@caratPos> ^ <errorMessage>
|
/// <whitespace@caratPos> ^ <errorMessage>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="lineStart"></param>
|
|
||||||
/// <param name="caratPos"></param>
|
|
||||||
/// <param name="errorMessage"></param>
|
|
||||||
/// <param name="source"></param>
|
|
||||||
static void WriteDebugLine(long lineStart, long caratPos, string errorMessage, Stream source)
|
static void WriteDebugLine(long lineStart, long caratPos, string errorMessage, Stream source)
|
||||||
{
|
{
|
||||||
source.Position = lineStart;
|
source.Position = lineStart;
|
||||||
@ -528,19 +512,13 @@ namespace Assignment_1
|
|||||||
source.Position = lineStart;
|
source.Position = lineStart;
|
||||||
source.SetLength(source.Position);
|
source.SetLength(source.Position);
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
#region Data Handling
|
|
||||||
// Data Handling
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parses & evaluates the expression from the stream, moving the stream to the end of the last value
|
/// Parses & evaluates the expression from the stream, moving the stream to the end of the last value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s"></param>
|
|
||||||
/// <param name="expression"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
long FindExpression(Stream s, out string expression)
|
long FindExpression(Stream s, out string expression)
|
||||||
{
|
{
|
||||||
string result = "";
|
string result = "";
|
||||||
// iterate through values until we reach either the end of the stream or the end-of-statement
|
|
||||||
bool IsAppendSet = true;
|
bool IsAppendSet = true;
|
||||||
while (s.Position < s.Length && !IsNextEoS(s))
|
while (s.Position < s.Length && !IsNextEoS(s))
|
||||||
{
|
{
|
||||||
@ -566,17 +544,14 @@ namespace Assignment_1
|
|||||||
{
|
{
|
||||||
throw new ParserException("Append operator not set", 0, s.Position);
|
throw new ParserException("Append operator not set", 0, s.Position);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expression = result;
|
expression = result;
|
||||||
return s.Position;
|
return s.Position;
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks ahead to see if the next non-whitespace character is the EoS indicator (';')
|
/// Checks ahead to see if the next non-whitespace character is the EoS indicator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s"></param>
|
|
||||||
/// <param name="EoSChar"></param>
|
|
||||||
/// <returns>true if the next char is <paramref name="EoSChar"/>, else false</returns>
|
/// <returns>true if the next char is <paramref name="EoSChar"/>, else false</returns>
|
||||||
static bool IsNextEoS(Stream s, char EoSChar = ';')
|
static bool IsNextEoS(Stream s, char EoSChar = ';')
|
||||||
{
|
{
|
||||||
@ -592,11 +567,8 @@ namespace Assignment_1
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the next value in the stream
|
/// Finds the next value expression in the stream
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s"></param>
|
|
||||||
/// <param name="returnedValue"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
long FindValue(Stream s, out string returnedValue)
|
long FindValue(Stream s, out string returnedValue)
|
||||||
{
|
{
|
||||||
SkipWhitespace(s);
|
SkipWhitespace(s);
|
||||||
@ -609,25 +581,21 @@ namespace Assignment_1
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
long t = FindExistingIdentifier(s, out string keyValue);
|
long t = FindExistingIdentifier(s, out string keyValue);
|
||||||
// Set the key value to result + this read string
|
|
||||||
//keyValue = result + keyValue;
|
|
||||||
|
|
||||||
if (!Symbols.ContainsKey(keyValue))
|
if (!Symbols.ContainsKey(keyValue))
|
||||||
{
|
{
|
||||||
throw new ParserException("Could not find key: " + keyValue, 0, s.Position);
|
throw new ParserException("Could not find key: " + keyValue, 0, s.Position);
|
||||||
}
|
}
|
||||||
returnedValue = Symbols[keyValue].Item1;
|
returnedValue = Symbols[keyValue].Item1;
|
||||||
return t;
|
return t;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long FindIdentifier(Stream s, out string returnedKey)
|
static long FindIdentifier(Stream s, out string returnedKey)
|
||||||
{
|
{
|
||||||
long wordEnd = FindNextWord(s, out returnedKey);
|
long wordEnd = FindNextWord(s, out returnedKey);
|
||||||
return wordEnd;
|
return wordEnd;
|
||||||
}
|
}
|
||||||
long FindExistingIdentifier(Stream s, out string returnedKey)
|
static long FindExistingIdentifier(Stream s, out string returnedKey)
|
||||||
{
|
{
|
||||||
long wordEnd = FindNextWord(s, out string identifier);
|
long wordEnd = FindNextWord(s, out string identifier);
|
||||||
if (identifier.Length > 1 && identifier.EndsWith(';'))
|
if (identifier.Length > 1 && identifier.EndsWith(';'))
|
||||||
@ -637,7 +605,6 @@ namespace Assignment_1
|
|||||||
wordEnd--;
|
wordEnd--;
|
||||||
s.Position--;
|
s.Position--;
|
||||||
}
|
}
|
||||||
// Lookup the value in the symbol table
|
|
||||||
returnedKey = identifier;
|
returnedKey = identifier;
|
||||||
return wordEnd;
|
return wordEnd;
|
||||||
}
|
}
|
||||||
@ -645,10 +612,7 @@ namespace Assignment_1
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the end of the complete literal definition, returning the stream to the original position
|
/// Finds the end of the complete literal definition, returning the stream to the original position
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s"></param>
|
static long FindLiteral(Stream s, out string returnedLiteral)
|
||||||
/// <param name="returnedLiteral"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
long FindLiteral(Stream s, out string returnedLiteral)
|
|
||||||
{
|
{
|
||||||
long pos = s.Position;
|
long pos = s.Position;
|
||||||
// Is a literal. Now we must parse until we find the end of the literal
|
// Is a literal. Now we must parse until we find the end of the literal
|
||||||
@ -658,14 +622,16 @@ namespace Assignment_1
|
|||||||
{
|
{
|
||||||
if (c == '\"')
|
if (c == '\"')
|
||||||
{
|
{
|
||||||
long pos = s.Position--;
|
s.Position--;
|
||||||
if (ReadChar(s) == '\\')
|
if (PreviousChar(s) == '\\')
|
||||||
{
|
{
|
||||||
// TODO: handle the \\ escape
|
// TODO: handle the \\ escape
|
||||||
|
s.Position++;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
s.Position++;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -673,7 +639,7 @@ namespace Assignment_1
|
|||||||
}, out string resultLiteral);
|
}, out string resultLiteral);
|
||||||
if (resultPosition > -1)
|
if (resultPosition > -1)
|
||||||
{
|
{
|
||||||
returnedLiteral = resultLiteral;
|
returnedLiteral = resultLiteral.Replace("\\\"", "\"");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -682,16 +648,11 @@ namespace Assignment_1
|
|||||||
s.Position = pos;
|
s.Position = pos;
|
||||||
return resultPosition;
|
return resultPosition;
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#region HelperFunctions
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads the memory stream as a UTF-8 encoded string until the next occurance of '\n' or '\r\n' (consuming, and excluded)
|
/// Reads the memory stream as a UTF-8 encoded string until the next occurance of '\n' or '\r\n' (consuming, and excluded)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
static string GetNextLine(Stream s)
|
static string GetNextLine(Stream s)
|
||||||
{
|
{
|
||||||
FindNextOccurance(s, '\n', out string nextLine);
|
FindNextOccurance(s, '\n', out string nextLine);
|
||||||
@ -701,26 +662,15 @@ namespace Assignment_1
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the end-boundary of the next word in the stream, and returns the stream to the original position
|
/// Finds the end-boundary of the next word in the stream, and returns the stream to the original position
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s"></param>
|
|
||||||
/// <param name="nextWord"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
static long FindNextWord(Stream s, out string nextWord)
|
static long FindNextWord(Stream s, out string nextWord)
|
||||||
{
|
{
|
||||||
StringBuilder newWord = new();
|
StringBuilder newWord = new StringBuilder();
|
||||||
// Record our current position
|
|
||||||
long start = s.Position;
|
long start = s.Position;
|
||||||
// Check if the character at the current pos is whitespace, if so, keep advancing until it isn't.
|
|
||||||
// NB: Whitespace includes carriage returns and line feeds,
|
|
||||||
// so 'set\r\n
|
|
||||||
// var
|
|
||||||
// "expression";
|
|
||||||
// should be valid
|
|
||||||
char currentChar = ReadChar(s);
|
char currentChar = ReadChar(s);
|
||||||
while (s.Position < s.Length && char.IsWhiteSpace(currentChar))
|
while (s.Position < s.Length && char.IsWhiteSpace(currentChar))
|
||||||
{
|
{
|
||||||
currentChar = ReadChar(s);
|
currentChar = ReadChar(s);
|
||||||
}
|
}
|
||||||
// Add the last read value to the SB
|
|
||||||
newWord.Append(currentChar);
|
newWord.Append(currentChar);
|
||||||
// Start a second loop, this time checking we're not a whitespace char
|
// Start a second loop, this time checking we're not a whitespace char
|
||||||
while (s.Position < s.Length)
|
while (s.Position < s.Length)
|
||||||
@ -747,10 +697,6 @@ namespace Assignment_1
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds and returns the position of the next occurance of the Func returning true.
|
/// Finds and returns the position of the next occurance of the Func returning true.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s"></param>
|
|
||||||
/// <param name="p">A 'predicate'-like Func</param>
|
|
||||||
/// <param name="result">Returns the string captured while searching for the next char</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
static long FindNextOccurance(Stream s, Func<char, Stream, bool> p, out string result)
|
static long FindNextOccurance(Stream s, Func<char, Stream, bool> p, out string result)
|
||||||
{
|
{
|
||||||
long start = s.Position;
|
long start = s.Position;
|
||||||
@ -780,21 +726,16 @@ namespace Assignment_1
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the next position of the character
|
/// Finds the next position of the supplied character
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s"></param>
|
|
||||||
/// <param name="c"></param>
|
|
||||||
/// <param name="result">Captures the string read in searching for the character</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
static long FindNextOccurance(Stream s, char c, out string result)
|
static long FindNextOccurance(Stream s, char c, out string result)
|
||||||
{
|
{
|
||||||
return FindNextOccurance(s, (streamChar, s) => streamChar == c, out result);
|
return FindNextOccurance(s, (streamChar, s) => streamChar == c, out result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads the next UTF-8 encoded character in the stream, and advances the stream by the amount of characters read
|
/// Reads the next UTF-8 encoded character in the stream, and advances the stream by the amount of characters read
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
static char ReadChar(Stream s)
|
static char ReadChar(Stream s)
|
||||||
{
|
{
|
||||||
// As UTF-8 allows codepoints to span multiple bytes, reading a single byte as a character will not always give the expected
|
// As UTF-8 allows codepoints to span multiple bytes, reading a single byte as a character will not always give the expected
|
||||||
@ -831,11 +772,10 @@ namespace Assignment_1
|
|||||||
string converted = Encoding.UTF8.GetString(charBytes);
|
string converted = Encoding.UTF8.GetString(charBytes);
|
||||||
return converted[0];
|
return converted[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads the next character in the stream, and returns the position to the original position
|
/// Reads the next character in the stream, and returns the position to the original position
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
static char PeekChar(Stream s)
|
static char PeekChar(Stream s)
|
||||||
{
|
{
|
||||||
long curr = s.Position;
|
long curr = s.Position;
|
||||||
@ -847,8 +787,6 @@ namespace Assignment_1
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads the previous char
|
/// Reads the previous char
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
static char PreviousChar(Stream s)
|
static char PreviousChar(Stream s)
|
||||||
{
|
{
|
||||||
Stack<byte> charBytes = new Stack<byte>(4);
|
Stack<byte> charBytes = new Stack<byte>(4);
|
||||||
@ -871,13 +809,12 @@ namespace Assignment_1
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Skips whitespace characters
|
/// Skips whitespace characters
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s"></param>
|
|
||||||
static void SkipWhitespace(Stream s)
|
static void SkipWhitespace(Stream s)
|
||||||
{
|
{
|
||||||
char c = PeekChar(s);
|
char c = PeekChar(s);
|
||||||
while (s.Position < s.Length && char.IsWhiteSpace(c))
|
while (s.Position < s.Length && char.IsWhiteSpace(c))
|
||||||
{
|
{
|
||||||
ReadChar(s); // move by the size of that character
|
ReadChar(s);
|
||||||
c = PeekChar(s);
|
c = PeekChar(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -895,7 +832,7 @@ namespace Assignment_1
|
|||||||
|
|
||||||
static List<string> GetStringLines(string source, int maxWidth)
|
static List<string> GetStringLines(string source, int maxWidth)
|
||||||
{
|
{
|
||||||
List<string> lines = new();
|
List<string> lines = new List<string>();
|
||||||
int j = 0;
|
int j = 0;
|
||||||
while (j < source.Length)
|
while (j < source.Length)
|
||||||
{
|
{
|
||||||
@ -905,14 +842,11 @@ namespace Assignment_1
|
|||||||
}
|
}
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
public class ParserException : Exception
|
public class ParserException : Exception
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Importance is used to signify how the parser should respond to the error.
|
/// Custom expression that will also pass the LinePosition & importance of the error (not implemented)
|
||||||
/// A code of 3 or greater is a critical error; the application will throw the error further up the call and exit.
|
|
||||||
/// 0 implies the line may be retried.
|
|
||||||
/// 1 should imply the current block is not valid and should be retried.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Importance = 0;
|
public int Importance = 0;
|
||||||
public long LinePosition = -1;
|
public long LinePosition = -1;
|
||||||
|
@ -3,11 +3,6 @@
|
|||||||
"Assignment 1": {
|
"Assignment 1": {
|
||||||
"commandName": "Project"
|
"commandName": "Project"
|
||||||
},
|
},
|
||||||
"newProfile1": {
|
|
||||||
"commandName": "Executable",
|
|
||||||
"executablePath": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
|
|
||||||
"commandLineArgs": "-command \"& type .\\ExampleOutput.txt | & '.\\Assignment 1.exe'\""
|
|
||||||
},
|
|
||||||
"Example1": {
|
"Example1": {
|
||||||
"commandName": "Executable",
|
"commandName": "Executable",
|
||||||
"executablePath": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
|
"executablePath": "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
|
||||||
|
49
Assignment 1/example4.txt
Normal file
49
Assignment 1/example4.txt
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
set a1 "
|
||||||
|
To be, or not to be - that is the question:
|
||||||
|
Whether 'tis nobler in the mind to suffer
|
||||||
|
The slings and arrows of outrageous fortune
|
||||||
|
Or to take arms against a sea of troubles,
|
||||||
|
And by opposing end them. To die - to sleep -
|
||||||
|
No more; and by a sleep to say we end
|
||||||
|
The heartache, and the thousand natural shocks
|
||||||
|
That flesh is heir to. 'Tis a consummation
|
||||||
|
Devoutly to be wish'd. To die - to sleep.
|
||||||
|
To sleep - perchance to dream: ay, there's the rub!
|
||||||
|
For in that sleep of death what dreams may come
|
||||||
|
When we have shuffled off this mortal coil,
|
||||||
|
Must give us pause. There's the respect
|
||||||
|
That makes calamity of so long life.
|
||||||
|
For who would bear the whips and scorns of time,
|
||||||
|
The oppressor's wrong, the proud man's contumely,
|
||||||
|
The pangs of despised love, the law's delay,
|
||||||
|
The insolence of office, and the spurns
|
||||||
|
That patient merit of the unworthy takes,
|
||||||
|
When he himself might his quietus make" + NEWLINE +
|
||||||
|
"With a bare bodkin?
|
||||||
|
";
|
||||||
|
|
||||||
|
set averylongvariablethathasbeencontrivedjusttobeanuisancetothe341classwhohaveprobablythoughtthattheycouldgetawaywithallocatingshortvariablenamesofashortlengthbutaregoingtobereallydisappointediftheydidbecausethisisnotashortvariableidentifieratall "
|
||||||
|
Who would these fardels bear,
|
||||||
|
To grunt and sweat under a weary life,
|
||||||
|
But that the dread of something after death -
|
||||||
|
The undiscover'd country, from whose bourn
|
||||||
|
No traveller returns - puzzles the will,
|
||||||
|
And makes us rather bear those ills we have
|
||||||
|
Than fly to others that we know not of?
|
||||||
|
Thus conscience does make cowards of us all,
|
||||||
|
And thus the native hue of resolution
|
||||||
|
Is sicklied o'er with the pale cast of thought,
|
||||||
|
And enterprises of great pith and moment
|
||||||
|
With this regard their currents turn awry
|
||||||
|
And lose the name of action.";
|
||||||
|
|
||||||
|
set soliloquy a1 + NEWLINE + averylongvariablethathasbeencontrivedjusttobeanuisancetothe341classwhohaveprobablythoughtthattheycouldgetawaywithallocatingshortvariablenamesofashortlengthbutaregoingtobereallydisappointediftheydidbecausethisisnotashortvariableidentifieratall;
|
||||||
|
|
||||||
|
set dummy "dummy1";
|
||||||
|
|
||||||
|
print soliloquy;
|
||||||
|
printwordcount soliloquy;
|
||||||
|
printwords soliloquy;
|
||||||
|
printlength soliloquy;
|
||||||
|
list;
|
||||||
|
exit;
|
67
readme.md
67
readme.md
@ -1,5 +1,64 @@
|
|||||||
[![Build status](https://ci.software.kauripeak.co.nz/api/projects/status/44sgyt4dadc2il99?svg=true)](https://ci.software.kauripeak.co.nz/project/AppVeyor/159-341-assignment-1)
|
# 159.341 Assignment 1 - String Interpreter [![Build status](https://ci.software.kauripeak.co.nz/api/projects/status/44sgyt4dadc2il99?svg=true)](https://ci.software.kauripeak.co.nz/project/AppVeyor/159-341-assignment-1)
|
||||||
# 159.341 Assignment 1 - String Interpreter
|
## A basic interpreter for a simple programming language.
|
||||||
|
|
||||||
A basic JIT interpreter for a simple, custom programming language.
|
There is only one implicit type definition - strings.
|
||||||
There exists only one implicit type definition - strings.
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
This program is written in C# (.NET Core 3.0*) and specifically avoids the use of the `regex` class and the
|
||||||
|
|
||||||
|
```cs
|
||||||
|
string.split()
|
||||||
|
```
|
||||||
|
method to show an understanding of the implementation.
|
||||||
|
|
||||||
|
\* *.NET Core 3.0 **should** be supported on the lab machines*
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
The application accepts piped input streams via `StreamReader` and piped input via `type <file> | <application>`
|
||||||
|
|
||||||
|
There are preconfigured build options that will automatically pipe the example files:
|
||||||
|
|
||||||
|
[![VS Build Configuration](https://git.software.kauripeak.co.nz/content/upload/159.341/159-341-assignment-1-build-configuration.png)](https://git.software.kauripeak.co.nz/content/upload/159.341/159-341-assignment-1-build-configuration.png)
|
||||||
|
|
||||||
|
These examples are automatically copied to the output directory at build.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
This stream is navigated character-by-character (not byte-by-byte)
|
||||||
|
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
```py
|
||||||
|
set somevar "NewValue";
|
||||||
|
set othervar somevar + SPACE + ";";
|
||||||
|
list;
|
||||||
|
exit;
|
||||||
|
```
|
||||||
|
Would output:
|
||||||
|
```
|
||||||
|
┌─────────────┬──────────────────────┬─────────┐
|
||||||
|
│ Symbol │ Value │ Flags │
|
||||||
|
├─────────────┼──────────────────────┼─────────┤
|
||||||
|
│somevar │NewValue │00000000 │
|
||||||
|
├─────────────┼──────────────────────┼─────────┤
|
||||||
|
│othervar │NewValue ; │00000000 │
|
||||||
|
└─────────────┴──────────────────────┴─────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## 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).
|
||||||
|
|
||||||
|
**NB:**
|
||||||
|
* A double quote can be inserted into the literal by using a backslash immediately beforehand (i.e. `\"`)
|
||||||
|
```cs
|
||||||
|
set someVar "this literal \" has a value";
|
||||||
|
```
|
||||||
|
prints
|
||||||
|
|
||||||
|
`this literal " has a value`
|
||||||
|
Loading…
x
Reference in New Issue
Block a user