Logical/Interpreter

From Esolang
Jump to navigation Jump to search

The following is version 0.1 of a Logical interpreter in C#. It was implemented within 6 hours, and thus I do not guarantee that it is bug free. Please let me know of any bugs in the interpreter.

/*
Copyright 2017 David Catt

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
namespace Logical {
	public static class Extensions {
		public static IEnumerable<Tuple<T, T>> GetPairs<T>(this IEnumerable<T> source) {
			using(var iterator = source.GetEnumerator()) {
				while(iterator.MoveNext()) {
					T first = iterator.Current;
					T second = iterator.MoveNext() ? iterator.Current : default(T);
					yield return Tuple.Create(first, second);
				}
			}
		}
		public static IEnumerable<Tuple<char, char>> GetPairs(this string source) {
			using(var iterator = source.GetEnumerator()) {
				while(iterator.MoveNext()) {
					char first = iterator.Current;
					char second = iterator.MoveNext() ? iterator.Current : default(char);
					yield return Tuple.Create(first, second);
				}
			}
		}
	}
	public enum LogicalErrorCode : byte {
		Success,
		InternalError,
		OpenFailed,
		ReadFailed,
		WriteFailed,
		LowercaseFound,
		NervousBreakdown,
		InvalidArgumentCount,
		InvalidCommand,
		LabelAlreadyExists,
		LabelDoesNotExist,
		VariableAlreadyExists,
		VariableDoesNotExist,
		TypeMismatch,
		InvalidTypeOperation,
		DivideByZero,
		FileAlreadyOpen,
		FileNotOpen,
		StackEmpty,
		InvalidOperation,
		InvalidLabelName,
		InvalidCondition,
		ValueFormatFailed,
		TypeDoesNotExist
	}
	public enum LogicalOp : byte {
		NOP,
		COPY,
		ADD,
		SUB,
		MUL,
		DIV,
		CONV,
		VAR,
		UNVAR,
		IF,
		LBL,
		JMP,
		CALL,
		RET,
		CIN,
		COUT,
		OPEN,
		FIN,
		FOUT,
		CLOSE,
		EXIT
	}
	public enum LogicalCondition {
		Equal,
		Inequal,
		Lesser,
		Greater,
		LesserOrEqual,
		GreaterOrEqual
	}
	public struct LogicalLine {
		public readonly LogicalOp Operation;
		public readonly object Value1;
		public readonly object Value2;
		public readonly object Value3;
		internal LogicalLine(LogicalOp op) { Operation = op; Value1 = Value2 = Value3 = null; }
		internal LogicalLine(LogicalOp op, object v1) { Operation = op; Value1 = v1; Value2 = Value3 = null; }
		internal LogicalLine(LogicalOp op, object v1, object v2) { Operation = op; Value1 = v1; Value2 = v2; Value3 = null; }
		internal LogicalLine(LogicalOp op, object v1, object v2, object v3) { Operation = op; Value1 = v1; Value2 = v2; Value3 = v3; }
	}
	public struct LogicalVariable {
		public readonly string Name;
		public override string ToString() => Name;
		internal LogicalVariable(string name) { Name = name; }
	}
	public struct LogicalError {
		public static readonly LogicalError SUCCESS = new LogicalError(LogicalErrorCode.Success, 0);
		public readonly LogicalErrorCode Code;
		public readonly ulong Line;
		public readonly Exception Info;
		internal LogicalError(LogicalErrorCode code, ulong line, Exception info = null) {
			Code = code;
			Line = line;
			Info = info;
		}
	}
	public sealed class LogicalProgram {
		public LogicalLine[] Lines { get; private set; }
		public LogicalError Error { get; private set; }
		internal LogicalProgram(LogicalLine[] lines) { Lines = lines; Error = LogicalError.SUCCESS; }
		internal LogicalProgram(LogicalError error) { Lines = null; Error = error; }
	}
	public sealed class LogicalFile : IDisposable {
		private readonly FileStream stream;
		private readonly StreamReader reader;
		private readonly StreamWriter writer;
		private bool closed = false;
		public string ReadLine() { lock(stream) return reader.ReadLine(); }
		public void WriteLine(string line) { lock(stream) writer.WriteLine(line); }
		public void Close() {
			lock(stream) {
				if(!closed) {
					reader.Close();
					writer.Close();
					stream.Close();
				}
			}
		}
		public void Dispose() => Close();
		public LogicalFile(string path) {
			stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
			try {
				reader = new StreamReader(stream);
				try {
					writer = new StreamWriter(stream);
				} catch { reader.Dispose(); throw; }
			} catch { stream.Dispose(); throw; }
		}
	}
	public sealed class LogicalRunner {
		private struct LogicalDecoded {
			public readonly LogicalLine Line;
			public readonly LogicalError Error;
			internal LogicalDecoded(LogicalLine line) { Line = line; Error = LogicalError.SUCCESS; }
			internal LogicalDecoded(LogicalError error) { Line = new LogicalLine(LogicalOp.NOP); Error = error; }
		}
		private static Tuple<char, char>[] MakeRot13Table() {
			int idx = 0; Tuple<char, char>[] list = new Tuple<char, char>[52];
			for(char a = 'a', b = 'n', c = 'A', d = 'N'; b <= 'z'; ++a, ++b, ++c, ++d) {
				list[idx++] = new Tuple<char, char>(a, b);
				list[idx++] = new Tuple<char, char>(b, a);
				list[idx++] = new Tuple<char, char>(c, d);
				list[idx++] = new Tuple<char, char>(d, c);
			}
			return list;
		}
		private static readonly IReadOnlyCollection<char> BADCHARS = (IReadOnlyCollection<char>)new HashSet<char>("abcdefghijklmnopqrstuvwxyz".ToCharArray());
		private static readonly ReadOnlyDictionary<char, char> SHIFTNUMBER = new ReadOnlyDictionary<char, char>("!1@2#3$4%5^6&7*8(9)0".GetPairs().ToDictionary(x => x.Item1, x => x.Item2));
		private static readonly ReadOnlyDictionary<char, char> ROT13TABLE = new ReadOnlyDictionary<char, char>(MakeRot13Table().ToDictionary(x => x.Item1, x => x.Item2));
		private static readonly ReadOnlyDictionary<string, LogicalCondition> CONDITIONS = new ReadOnlyDictionary<string, LogicalCondition>(new Tuple<string, LogicalCondition>[] {
			new Tuple<string, LogicalCondition>("==", LogicalCondition.Equal),
			new Tuple<string, LogicalCondition>("!=", LogicalCondition.Inequal),
			new Tuple<string, LogicalCondition>("<<", LogicalCondition.Lesser),
			new Tuple<string, LogicalCondition>("<=", LogicalCondition.LesserOrEqual),
			new Tuple<string, LogicalCondition>(">>", LogicalCondition.Greater),
			new Tuple<string, LogicalCondition>(">=", LogicalCondition.GreaterOrEqual)
		}.ToDictionary(x => x.Item1, x => x.Item2));
		private static object InputValue(string value) {
			if(value.Length > 1 && value[0] == '"' && value[value.Length - 1] == '"') {
				//Integer
				if(int.TryParse(value.Substring(1, value.Length - 2), out int num)) return -num;
				else return null;
			} else return value; //String
			//There is no combination that will return a LogicalVariable
		}
		private static string DecodeString(string value) {
			StringBuilder str = new StringBuilder(value.Length);
			for(int idx = value.Length; idx-- > 0;) str.Append(value[idx] == '_' ? ' ' : value[idx]);
			return str.ToString();
		}
		private static string ROT13(string value) {
			StringBuilder str = new StringBuilder(value.Length);
			foreach(char c in value) str.Append(ROT13TABLE.TryGetValue(c, out char n) ? n : c);
			return str.ToString();
		}
		private static bool LabelValidate(string value) => int.TryParse(value, out int _);
		private static string LabelNumber(string value) {
			StringBuilder str = new StringBuilder(value.Length + 1);
			str.Append('-');
			foreach(char c in value) if(SHIFTNUMBER.TryGetValue(c, out char n)) str.Append(n); else return null;
			return str.ToString();
		}
		private static readonly Dictionary<string, Func<string[], LogicalDecoded>>[] DECODERS = new Dictionary<string, Func<string[], LogicalDecoded>>[] {
			new Tuple<string, Func<string[], LogicalDecoded>>[] {
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"div",
					(s) => new LogicalDecoded(new LogicalLine(LogicalOp.EXIT))),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"conv",
					(s) => new LogicalDecoded(new LogicalLine(LogicalOp.RET))),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"error",
					(s) => new LogicalDecoded(new LogicalLine(LogicalOp.NOP)))
			}.ToDictionary(x => x.Item1, x => x.Item2),
			new Tuple<string, Func<string[], LogicalDecoded>>[] {
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"add",
					(s) => {
						string lbl = LabelNumber(s[2]);
						return lbl == null ? new LogicalDecoded(new LogicalError(LogicalErrorCode.InvalidLabelName, 0)) : new LogicalDecoded(new LogicalLine(LogicalOp.LBL, lbl));
					}),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"sub",
					(s) => new LogicalDecoded(new LogicalLine(LogicalOp.VAR, s[2]))),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"exit",
					(s) => new LogicalDecoded(new LogicalLine(LogicalOp.CIN, ROT13(s[2])))),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"defn",
					(s) => {
						object value = InputValue(s[2]);
						return value == null ? new LogicalDecoded(new LogicalError(LogicalErrorCode.ValueFormatFailed, 0)) : new LogicalDecoded(new LogicalLine(LogicalOp.COUT, value));
					}),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"inpt",
					(s) => LabelValidate(s[2]) ? new LogicalDecoded(new LogicalLine(LogicalOp.JMP, s[2])) : new LogicalDecoded(new LogicalError(LogicalErrorCode.InvalidLabelName, 0))),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"open",
					(s) => LabelValidate(s[2]) ? new LogicalDecoded(new LogicalLine(LogicalOp.CALL, s[2])) : new LogicalDecoded(new LogicalError(LogicalErrorCode.InvalidLabelName, 0))),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"read",
					(s) => new LogicalDecoded(new LogicalLine(LogicalOp.CLOSE, s[2]))),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"writ",
					(s) => new LogicalDecoded(new LogicalLine(LogicalOp.UNVAR, ROT13(s[2]))))
			}.ToDictionary(x => x.Item1, x => x.Item2),
			new Tuple<string, Func<string[], LogicalDecoded>>[] {
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"goto",
					(s) =>  {
						object value = InputValue(s[3]);
						return value == null ? new LogicalDecoded(new LogicalError(LogicalErrorCode.ValueFormatFailed, 0)) : new LogicalDecoded(new LogicalLine(LogicalOp.COPY, ROT13(s[2]), value));
					}),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"outp",
					(s) => new LogicalDecoded(new LogicalLine(LogicalOp.OPEN, s[2], s[3]))),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"func",
					(s) => new LogicalDecoded(new LogicalLine(LogicalOp.CONV, ROT13(s[2]), s[3]))),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"retn",
					(s) => new LogicalDecoded(new LogicalLine(LogicalOp.FIN, s[2], ROT13(s[3])))),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"clos",
					(s) => {
						object value = InputValue(s[3]);
						return value == null ? new LogicalDecoded(new LogicalError(LogicalErrorCode.ValueFormatFailed, 0)) : new LogicalDecoded(new LogicalLine(LogicalOp.FOUT, s[2], value));
					})
			}.ToDictionary(x => x.Item1, x => x.Item2),
			new Tuple<string, Func<string[], LogicalDecoded>>[] {
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"set",
					(s) => {
						object value1 = InputValue(s[3]);
						if(value1 == null) return new LogicalDecoded(new LogicalError(LogicalErrorCode.ValueFormatFailed, 0));
						object value2 = InputValue(s[4]);
						if(value2 == null) return new LogicalDecoded(new LogicalError(LogicalErrorCode.ValueFormatFailed, 0));
						return new LogicalDecoded(new LogicalLine(LogicalOp.ADD, ROT13(s[2]), value1, value2));
					}),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"mul",
					(s) => {
						object value1 = InputValue(s[3]);
						if(value1 == null) return new LogicalDecoded(new LogicalError(LogicalErrorCode.ValueFormatFailed, 0));
						object value2 = InputValue(s[4]);
						if(value2 == null) return new LogicalDecoded(new LogicalError(LogicalErrorCode.ValueFormatFailed, 0));
						return new LogicalDecoded(new LogicalLine(LogicalOp.SUB, ROT13(s[2]), value1, value2));
					}),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"undf",
					(s) => {
						object value1 = InputValue(s[3]);
						if(value1 == null) return new LogicalDecoded(new LogicalError(LogicalErrorCode.ValueFormatFailed, 0));
						object value2 = InputValue(s[4]);
						if(value2 == null) return new LogicalDecoded(new LogicalError(LogicalErrorCode.ValueFormatFailed, 0));
						return new LogicalDecoded(new LogicalLine(LogicalOp.MUL, ROT13(s[2]), value1, value2));
					}),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"labl",
					(s) => {
						LogicalCondition condition;
						return CONDITIONS.TryGetValue(s[3], out condition) ?
							new LogicalDecoded(new LogicalLine(LogicalOp.IF, InputValue(s[2]), condition, InputValue(s[4]))) :
							new LogicalDecoded(new LogicalError(LogicalErrorCode.InvalidCondition, 0));
					}),
				new Tuple<string, Func<string[], LogicalDecoded>>(
					"doif",
					(s) => {
						object value1 = InputValue(s[3]);
						if(value1 == null) return new LogicalDecoded(new LogicalError(LogicalErrorCode.ValueFormatFailed, 0));
						object value2 = InputValue(s[4]);
						if(value2 == null) return new LogicalDecoded(new LogicalError(LogicalErrorCode.ValueFormatFailed, 0));
						return new LogicalDecoded(new LogicalLine(LogicalOp.DIV, ROT13(s[2]), value1, value2));
					}),
			}.ToDictionary(x => x.Item1, x => x.Item2)
		};
		private object GetValue(object value, Dictionary<string, object> variables) =>
			value == null ? null :
			value.GetType().Equals(typeof(LogicalVariable)) ? variables.TryGetValue(((LogicalVariable)value).Name, out object varValue) ? varValue : null :
			value.GetType().Equals(typeof(string)) ? variables.TryGetValue(ROT13((string)value), out object varCheck) ? varCheck : DecodeString((string)value) : value; //value
		private LogicalErrorCode SetValue(Dictionary<string, object> variables, string name, object value) {
			if(!variables.TryGetValue(name, out object previous)) return LogicalErrorCode.VariableDoesNotExist;
			if(previous != null) {
				if(!previous.GetType().Equals(value.GetType())) return LogicalErrorCode.TypeMismatch;
				variables[name] = value;
			} else if(value.GetType().Equals(typeof(int))) variables[name] = "";
			else if(value.GetType().Equals(typeof(string))) variables[name] = 0;
			else return LogicalErrorCode.TypeDoesNotExist;
			return LogicalErrorCode.Success;
			/*if(!variables.TryGetValue(name, out object previous)) return LogicalErrorCode.VariableDoesNotExist;
			if(previous != null && previous.GetType() != value.GetType()) return LogicalErrorCode.TypeMismatch;
			variables[name] = value; return LogicalErrorCode.Success;*/
		}
		private bool Compare<T>(T v1, LogicalCondition c, T v2) where T : IComparable<T> {
			switch(c) {
				case LogicalCondition.Equal: return v1.CompareTo(v2) == 0;
				case LogicalCondition.Greater: return v1.CompareTo(v2) > 0;
				case LogicalCondition.GreaterOrEqual: return v1.CompareTo(v2) >= 0;
				case LogicalCondition.Inequal: return v1.CompareTo(v2) != 0;
				case LogicalCondition.Lesser: return v1.CompareTo(v2) < 0;
				case LogicalCondition.LesserOrEqual: return v1.CompareTo(v2) <= 0;
				default: return false;
			}
		}
		public LogicalError Run(LogicalProgram program) {
			if(program.Error.Code != LogicalErrorCode.Success) return new LogicalError();
			ulong line = 0;
			try {
				//Initialize state
				Dictionary<string, LogicalFile> files = new Dictionary<string, LogicalFile>();
				Dictionary<string, object> variables = new Dictionary<string, object>();
				Stack<ulong> stack = new Stack<ulong>();
				string lastName = "logical";
				LogicalLine[] code = program.Lines;
				ulong size = (ulong)code.LongLength;
				LogicalError globalError = LogicalError.SUCCESS;
				try {
					//Execute program
					for(;;) {
						//Determine proper line number
						while(line >= size) {
							/*if(stack.Count > 0) line = stack.Pop();
							else */
							goto done;
						}
						//Execute line
						LogicalLine proc = code[line];
						object value1, value2;
						string strVal;
						LogicalErrorCode error;
						LogicalFile file;
						switch(proc.Operation) { //Note: a good optimizer will convert this into a call table (size=256)
							case LogicalOp.ADD:
								value1 = GetValue(proc.Value2, variables);
								if(value1 == null) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
								value2 = GetValue(proc.Value3, variables);
								if(value2 == null) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
								if(!value1.GetType().Equals(value2.GetType())) { globalError = new LogicalError(LogicalErrorCode.TypeMismatch, line); goto done; }
								error = LogicalErrorCode.InvalidTypeOperation;
								if(value1.GetType().Equals(typeof(string))) error = SetValue(variables, (string)proc.Value1, (string)value1 + (string)value2);
								else if(value2.GetType().Equals(typeof(int))) error = SetValue(variables, (string)proc.Value1, (int)value1 + (int)value2);
								if(error != LogicalErrorCode.Success) { globalError = new LogicalError(error, line); goto done; }
								break;
							case LogicalOp.CALL:
								stack.Push(line);
								line = (ulong)proc.Value1;
								continue;
							case LogicalOp.CIN:
								error = SetValue(variables, (string)proc.Value1, Console.ReadKey().KeyChar.ToString());
								if(error != LogicalErrorCode.Success) { globalError = new LogicalError(error, line); goto done; }
								break;
							case LogicalOp.CLOSE:
								if(files.TryGetValue((string)proc.Value1, out file)) {
									file.Close();
									files.Remove((string)proc.Value1);
									break;
								} else { globalError = new LogicalError(LogicalErrorCode.FileNotOpen, line); goto done; }
							case LogicalOp.CONV:
								value1 = GetValue(proc.Value2, variables);
								if(value1 == null) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
								if(!value1.GetType().Equals(typeof(int))) { globalError = new LogicalError(LogicalErrorCode.InvalidTypeOperation, line); goto done; }
								value1 = (char)(int)value1;
								error = SetValue(variables, (string)proc.Value1, value1);
								if(error != LogicalErrorCode.Success) { globalError = new LogicalError(error, line); goto done; }
								break;
							case LogicalOp.COPY:
								value1 = GetValue(proc.Value2, variables);
								if(value1 == null) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
								error = SetValue(variables, (string)proc.Value1, value1);
								if(error != LogicalErrorCode.Success) { globalError = new LogicalError(error, line); goto done; }
								break;
							case LogicalOp.COUT:
								object value = GetValue(proc.Value1, variables);
								if(value == null) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
								Console.Write(value);
								break;
							case LogicalOp.DIV:
								value1 = GetValue(proc.Value2, variables);
								if(value1 == null) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
								value2 = GetValue(proc.Value3, variables);
								if(value2 == null) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
								if(!value1.GetType().Equals(value2.GetType())) { globalError = new LogicalError(LogicalErrorCode.TypeMismatch, line); goto done; }
								error = LogicalErrorCode.InvalidTypeOperation;
								if(value1.GetType().Equals(typeof(string))) error = LogicalErrorCode.InvalidTypeOperation;
								else if(value2.GetType().Equals(typeof(int))) {
									if((int)value2 == 0) { globalError = new LogicalError(LogicalErrorCode.DivideByZero, line); goto done; }
									error = SetValue(variables, (string)proc.Value1, (int)value1 / (int)value2);
								}
								if(error != LogicalErrorCode.Success) { globalError = new LogicalError(error, line); goto done; }
								break;
							case LogicalOp.EXIT: goto done;
							case LogicalOp.FIN:
								if(files.TryGetValue((string)proc.Value1, out file)) {
									try { strVal = file.ReadLine(); }
									catch(Exception ex) { globalError = new LogicalError(LogicalErrorCode.ReadFailed, line, ex); goto done; }
									if(strVal == null) { globalError = new LogicalError(LogicalErrorCode.ReadFailed, line); goto done; }
									error = SetValue(variables, (string)proc.Value1, strVal);
									if(error != LogicalErrorCode.Success) { globalError = new LogicalError(error, line); goto done; }
									break;
								} else { globalError = new LogicalError(LogicalErrorCode.FileNotOpen, line); goto done; }
							case LogicalOp.FOUT:
								if(files.TryGetValue((string)proc.Value1, out file)) {
									value = GetValue(proc.Value2, variables);
									if(value == null) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
									try { file.WriteLine(value.ToString()); }
									catch(Exception ex) { globalError = new LogicalError(LogicalErrorCode.WriteFailed, line, ex); goto done; }
									break;
								} else { globalError = new LogicalError(LogicalErrorCode.FileNotOpen, line); goto done; }
							case LogicalOp.IF:
								value1 = GetValue(proc.Value1, variables);
								if(value1 == null) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
								value2 = GetValue(proc.Value3, variables);
								if(value2 == null) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
								if(!value1.GetType().Equals(value2.GetType())) { globalError = new LogicalError(LogicalErrorCode.TypeMismatch, line); goto done; }
								LogicalCondition condition = (LogicalCondition)proc.Value2;
								if(value1.GetType().Equals(typeof(string))) {
									if(Compare((string)value1, condition, (string)value2)) ++line;
									break;
								} else if(value1.GetType().Equals(typeof(int))) {
									if(Compare((int)value1, condition, (int)value2)) ++line;
									break;
								} else { globalError = new LogicalError(LogicalErrorCode.InvalidTypeOperation, line); goto done; }
							case LogicalOp.JMP: line = (ulong)proc.Value1; continue;
							case LogicalOp.MUL:
								value1 = GetValue(proc.Value2, variables);
								if(value1 == null) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
								value2 = GetValue(proc.Value3, variables);
								if(value2 == null) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
								if(!value1.GetType().Equals(value2.GetType())) { globalError = new LogicalError(LogicalErrorCode.TypeMismatch, line); goto done; }
								error = LogicalErrorCode.InvalidTypeOperation;
								if(value1.GetType().Equals(typeof(string))) error = LogicalErrorCode.InvalidTypeOperation;
								else if(value2.GetType().Equals(typeof(int))) error = SetValue(variables, (string)proc.Value1, (int)value1 * (int)value2);
								if(error != LogicalErrorCode.Success) { globalError = new LogicalError(error, line); goto done; }
								break;
							case LogicalOp.NOP: break;
							case LogicalOp.OPEN:
								if(files.ContainsKey((string)proc.Value1)) { globalError = new LogicalError(LogicalErrorCode.FileAlreadyOpen, line); goto done; }
								try { file = new LogicalFile((string)proc.Value2); }
								catch(Exception ex) { globalError = new LogicalError(LogicalErrorCode.OpenFailed, line, ex); goto done; }
								try { files.Add((string)proc.Value1, file); }
								catch { file.Close(); throw; }
								break;
							case LogicalOp.RET:
								if(stack.Count == 0) { globalError = new LogicalError(LogicalErrorCode.StackEmpty, line); goto done; }
								line = stack.Pop();
								continue;
							case LogicalOp.SUB:
								value1 = GetValue(proc.Value2, variables);
								if(value1 == null) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
								value2 = GetValue(proc.Value3, variables);
								if(value2 == null) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
								if(!value1.GetType().Equals(value2.GetType())) { globalError = new LogicalError(LogicalErrorCode.TypeMismatch, line); goto done; }
								error = LogicalErrorCode.InvalidTypeOperation;
								if(value1.GetType().Equals(typeof(string))) error = LogicalErrorCode.InvalidTypeOperation;
								else if(value2.GetType().Equals(typeof(int))) error = SetValue(variables, (string)proc.Value1, (int)value1 - (int)value2);
								if(error != LogicalErrorCode.Success) { globalError = new LogicalError(error, line); goto done; }
								break;
							case LogicalOp.UNVAR:
								if(!variables.ContainsKey((string)proc.Value1)) { globalError = new LogicalError(LogicalErrorCode.VariableDoesNotExist, line); goto done; }
								variables.Remove((string)proc.Value1);
								break;
							case LogicalOp.VAR:
								if(variables.ContainsKey(lastName)) { globalError = new LogicalError(LogicalErrorCode.VariableAlreadyExists, line); goto done; }
								variables.Add(lastName, null);
								lastName = (string)proc.Value1;
								break;
							default: globalError = new LogicalError(LogicalErrorCode.InvalidOperation, line); goto done;
						}
						++line;
					}
				} catch(Exception ex) { globalError = new LogicalError(LogicalErrorCode.InternalError, line, ex); }
				//Clean-up state
				done: foreach(LogicalFile file in files.Values) file.Dispose();
				files.Clear();
				variables.Clear();
				stack.Clear();
				//Exit
				return globalError;
			} catch(Exception ex) { return new LogicalError(LogicalErrorCode.InternalError, line, ex); }
		}
		public LogicalProgram Load(Stream stream) {
			ulong line = 0;
			try {
				List<LogicalLine> code = new List<LogicalLine>();
				Dictionary<string, ulong> labels = new Dictionary<string, ulong>();
				using(StreamReader reader = new StreamReader(stream)) {
					for(string raw; !reader.EndOfStream; ++line) {
						//Read line
						try { raw = reader.ReadLine(); } catch(Exception ex) { return new LogicalProgram(new LogicalError(LogicalErrorCode.ReadFailed, line, ex)); }
						if(raw == null) break;
						//Validate line
						foreach(char c in raw) {
							if(c == '~') return new LogicalProgram(new LogicalError(LogicalErrorCode.NervousBreakdown, line));
							else if(BADCHARS.Contains(c)) return new LogicalProgram(new LogicalError(LogicalErrorCode.LowercaseFound, line));
						}
						//Split line
						string[] split = raw.ToLowerInvariant().Split(' ');
						//Decode operation
						if(((ulong)split.LongLength) - 1 > (ulong)DECODERS.LongLength || split.LongLength == 0)
							return new LogicalProgram(new LogicalError(LogicalErrorCode.InvalidArgumentCount, line));
						if(!split[0].Equals("comet")) continue;
						if(split.LongLength < 2) return new LogicalProgram(new LogicalError(LogicalErrorCode.InvalidArgumentCount, line));
						Dictionary<string, Func<string[], LogicalDecoded>> potentials = DECODERS[(ulong)split.LongLength - 2];
						if(potentials == null) return new LogicalProgram(new LogicalError(LogicalErrorCode.InvalidArgumentCount, line));
						if(!potentials.TryGetValue(split[1], out Func<string[], LogicalDecoded> decoder))
							return new LogicalProgram(new LogicalError(LogicalErrorCode.InvalidCommand, line));
						LogicalDecoded decoded = decoder(split);
						if(decoded.Error.Code != LogicalErrorCode.Success) return new LogicalProgram(new LogicalError(decoded.Error.Code, line, decoded.Error.Info));
						if(decoded.Line.Operation == LogicalOp.LBL) labels.Add((string)decoded.Line.Value1, --line - 2);
						else code.Add(decoded.Line);
					}
				}
				//Update jumps
				LogicalLine[] lines = code.ToArray();
				code.Clear();
				for(ulong idx = 0, len = (ulong)lines.LongLength; idx < len; ++idx) {
					if(lines[idx].Operation == LogicalOp.JMP || lines[idx].Operation == LogicalOp.CALL) {
						if(lines[idx].Value1.GetType() == typeof(string) && labels.TryGetValue((string)lines[idx].Value1, out ulong pointer))
							lines[idx] = new LogicalLine(lines[idx].Operation, pointer);
						else return new LogicalProgram(new LogicalError(LogicalErrorCode.LabelDoesNotExist, idx));
					}
				}
				//Return program
				return new LogicalProgram(lines);
			} catch(Exception ex) { return new LogicalProgram(new LogicalError(LogicalErrorCode.InternalError, line, ex));  }
		}
	}
	class Program {
		static int DisplayHelp() {
			Console.WriteLine("usage:");
			Console.WriteLine("  LOGICAL.EXE DELETE [file]   : RUN PROGRAM");
			Console.WriteLine("  LOGICAL.EXE RUN    [file]   : DELETE PROGRAM");
			Console.WriteLine();
			return 1;
		}
		static int DisplayError() { Console.WriteLine("ERROR: THE OPERATION COMPLETED SUCCESSFULLY"); return 1; }
		static int Main(string[] args) {
			if(args.LongLength == 2) {
				if(args[0].Equals("DELETE")) {
					try {
						LogicalRunner runner = new LogicalRunner();
						using(FileStream file = new FileStream(args[1], FileMode.Open, FileAccess.Read, FileShare.Read)) {
							LogicalProgram program = runner.Load(file);
							return runner.Run(program).Code == LogicalErrorCode.Success ? 0 : DisplayError();
						}
					} catch { return DisplayError(); }
				} else if(args[0].Equals("RUN")) {
					try { File.Delete(args[1]); return 0; } catch { return DisplayError(); }
				} else return DisplayHelp();
			} else return DisplayHelp();
		}
	}
}