Skip to content

Instantly share code, notes, and snippets.

@GuyInGrey
Created April 28, 2020 22:18
Show Gist options
  • Save GuyInGrey/b13e77ec5597c6d0271892bc048fa855 to your computer and use it in GitHub Desktop.
Save GuyInGrey/b13e77ec5597c6d0271892bc048fa855 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using Microsoft.VisualBasic;
using Tether;
namespace Modules
{
public class BinaryBasedInterpreter : TetherModule, ITrayMenu
{
string[] Script;
Dictionary<byte, int> Labels;
byte[] Memory;
int CurrentLine;
byte[] Input;
bool Running;
int Delay = 10;
bool Processing = false;
string Buffer = "";
static Dictionary<byte, string> Instructions = new Dictionary<byte, string>()
{
{ 0b00000000, "exit" },
{ 0b00000001, "print" },
{ 0b00000010, "set" },
{ 0b00000011, "dump" },
{ 0b10000000, "printnumval" },
{ 0b00001000, "label" },
{ 0b00001001, "jump" },
{ 0b00001010, "jumpif" },
{ 0b00000100, "==" },
{ 0b00000101, "<" },
{ 0b00000110, ">" },
{ 0b00010000, "math" },
{ 0b00010001, "+" },
{ 0b00010010, "-" },
{ 0b00010011, "*" },
{ 0b00010100, "/" },
};
public override ModuleInfo GetInfo() => new ModuleInfo()
{
Name = "BinaryBasedInterpreter",
};
public void Reset()
{
Running = false;
Memory = new byte[256];
CurrentLine = 0;
Buffer = "";
Labels = new Dictionary<byte, int>();
for (var l = 0; l < Script.Length; l++)
{
var parts = GetParts(l);
if (parts.Item1 == 0 && parts.Item2 == 0b00001000)
{
parts = GetParts(l + 1);
Labels.Add(GetValue(l + 1).Value, l + 2);
}
}
}
public MenuItem GetMenu()
{
var menu = new MenuItem();
menu.Text = "Interpreter";
var items = new List<MenuItem>()
{
new MenuItem("Load Script", (a, b) =>
{
var s = Interaction.InputBox("What script what you like to load?","Input Script Name","");
Script = File.ReadAllText(@"Scripts\" + s).Split('\n');
Reset();
this.Log("Loaded Script: " + s);
}),
new MenuItem("Start/Resume", (a, b) =>
{
Running = true;
}),
};
items.ForEach(i => menu.MenuItems.Add(i));
return menu;
}
public override void Startup()
{
StartTimer(() =>
{
if (Running && !Processing)
{
Process();
}
}, 0, Delay);
}
public (int, byte) GetParts(int lineIndex)
{
try
{
var line = Script[lineIndex];
var comment = line.IndexOf("//");
if (comment != -1)
{
line = line.Substring(0, comment);
}
line = line.Trim();
line = Regex.Replace(line, @"\s+", string.Empty);
var intPart = line.Substring(0, 2);
var bytePart = line.Substring(2, 8);
return (Convert.ToInt32(intPart, 2), Convert.ToByte(bytePart, 2));
}
catch (Exception e)
{
Console.WriteLine(e.Message + "\n\n" + e.StackTrace);
this.Log("Parsing Error - Line " + CurrentLine);
Running = false;
return (100, 0);
}
}
public void Process()
{
if (CurrentLine >= Script.Length)
{
this.Log("Program hit ending and unsafely termianted. Use the exit instruction.");
Running = false;
return;
}
this.Log("Processing line " + CurrentLine);
Processing = true;
var parts = GetParts(CurrentLine);
if (parts.Item1 == 100) { return; }
if (parts.Item1 != 0)
{
this.Log("Expected Instruction Type - Line " + CurrentLine);
Running = false;
return;
}
if (!Instructions.ContainsKey(parts.Item2))
{
this.Log("Invalid Instruction - Line " + CurrentLine);
Running = false;
return;
}
var instruction = Instructions[parts.Item2];
switch (instruction)
{
case "exit":
this.Log("Execution Stopped By Script");
Running = false;
CurrentLine = 0;
break;
case "print":
var val = GetValue(CurrentLine + 1);
if (val is null) { return; }
if (val < 32 || val > 126) { CurrentLine++; break; }
Buffer += Encoding.ASCII.GetString(new[] { val.Value });
CurrentLine++;
break;
case "set":
var val2 = GetValue(CurrentLine + 1);
if (val2 is null) { return; }
var val3 = GetValue(CurrentLine + 2);
if (val3 is null) { return; }
Memory[val2.Value] = val3.Value;
CurrentLine += 2;
break;
case "dump":
if (Running)
{
this.Log(Buffer);
Buffer = "";
}
else
{
Buffer += "\n";
}
break;
case "printnumval":
val = GetValue(CurrentLine + 1);
if (val is null) { return; }
Buffer += ((int)val.Value).ToString();
CurrentLine++;
break;
case "jump":
var val4 = GetValue(CurrentLine + 1);
if (val4 is null) { return; }
CurrentLine = Labels[val4.Value] - 1;
break;
case "jumpif":
var val5 = GetValue(CurrentLine + 1);
if (val5 is null) { return; }
var val6 = GetValue(CurrentLine + 2);
if (val6 is null) { return; }
var op = Instructions[GetParts(CurrentLine + 3).Item2];
var val7 = GetValue(CurrentLine + 4);
if (val7 is null) { return; }
var result = false;
switch (op)
{
case "==":
result = val6 == val7;
break;
case "<":
result = val6 < val7;
break;
case ">":
result = val6 > val7;
break;
}
if (result) { CurrentLine = Labels[val5.Value] - 1; }
else { CurrentLine += 4; }
break;
case "math":
var toSet = GetValue(CurrentLine + 1);
if (toSet is null) { return; }
var left = GetValue(CurrentLine + 2);
if (left is null) { return; }
op = Instructions[GetParts(CurrentLine + 3).Item2];
var right = GetValue(CurrentLine + 4);
if (right is null) { return; }
switch (op)
{
case "+":
Memory[toSet.Value] = (byte)(left + right);
break;
case "-":
Memory[toSet.Value] = (byte)(left - right);
break;
case "*":
Memory[toSet.Value] = (byte)(left * right);
break;
case "/":
Memory[toSet.Value] = (byte)(left / right);
break;
}
CurrentLine += 4;
break;
case "label":
CurrentLine++;
break;
}
CurrentLine++;
Processing = false;
}
public byte? GetValue(int line)
{
var parts = GetParts(line);
if (parts.Item1 == 100) { return null; }
if (parts.Item1 == 1)
{
return parts.Item2;
}
else if (parts.Item1 == 2)
{
return Memory[parts.Item2];
}
else
{
this.Log("Expected Value Type - Line " + CurrentLine);
Running = false;
return byte.MaxValue;
}
}
public string Run(string input)
{
Script = input.Split('\n');
Reset();
var parts = GetParts(CurrentLine);
do
{
Process();
parts = GetParts(CurrentLine);
} while (!(parts.Item1 == 0 && parts.Item2 == 0 || CurrentLine >= Script.Length));
return Buffer;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment