using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Reflection; using NTERA.Core; using NTERA.Core.Interop; using NTERA.Engine.Compiler; namespace NTERA.Engine.Runtime.Base { public static class Keywords { private static Random random = new Random(); [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] private class KeywordAttribute : Attribute { public string Name { get; } public bool ImplicitString { get; } public bool ImplicitFormatted { get; } public KeywordAttribute(string name, bool implicitString = false, bool implicitFormatted = false) { if (implicitFormatted && !implicitString) throw new ArgumentException("Keyword cannot support formatting if it does not use implicit strings"); Name = name; ImplicitString = implicitString; ImplicitFormatted = implicitFormatted; } } public static Dictionary> StaticKeywords { get; } = _getKeywords(); private static Dictionary> _getKeywords() { var output = new Dictionary>(); foreach (MethodInfo method in typeof(Keywords).GetMethods(BindingFlags.Public | BindingFlags.Static)) { var keywords = method.GetCustomAttributes(); foreach (KeywordAttribute keyword in keywords) output[keyword.Name] = (Action)Delegate.CreateDelegate(typeof(Action), method); } return output; } [Keyword("RESTART")] public static void Restart(EraRuntime runtime, StackFrame context, ExecutionNode node) { runtime.Initialize(runtime.Console); runtime.Call(runtime.ExecutionProvider.DefinedProcedures.First(x => x.Name == "SYSTEM_TITLE")); } [Keyword("INPUT")] public static void Input(EraRuntime runtime, StackFrame context, ExecutionNode node) { runtime.InputResetEvent.WaitOne(); context.Variables["RESULT"][0] = runtime.LastInputValue; } [Keyword("INPUTS")] public static void InputString(EraRuntime runtime, StackFrame context, ExecutionNode node) { runtime.InputResetEvent.WaitOne(); context.Variables["RESULTS"][0] = runtime.LastInputValue; } [Keyword("LOADGLOBAL")] public static void LoadGlobal(EraRuntime runtime, StackFrame context, ExecutionNode node) { } [Keyword("SAVEGLOBAL")] public static void SaveGlobal(EraRuntime runtime, StackFrame context, ExecutionNode node) { } [Keyword("TIMES")] public static void Times(EraRuntime runtime, StackFrame context, ExecutionNode node) { Variable variable = runtime.ComputeVariable(context, node[0], out var index); variable[index] *= runtime.ComputeExpression(context, node[1]); } [Keyword("VARSIZE")] public static void Varsize(EraRuntime runtime, StackFrame context, ExecutionNode node) { Variable variable = runtime.ComputeVariable(context, node[0], out var index); int maxArrayDimensions = variable.Max(x => x.Key.Length); var resultVariable = runtime.GlobalVariables["RESULT"]; for (int i = 0; i < maxArrayDimensions; i++) { resultVariable[i] = variable.Where(x => x.Key.Length > i).Max(x => x.Key[i]) + 1; } } [Keyword("ALIGNMENT")] public static void Alignment(EraRuntime runtime, StackFrame context, ExecutionNode node) { string alignmentType = runtime.ComputeExpression(context, node[0]).String; if (!Enum.TryParse(alignmentType, true, out DisplayLineAlignment alignmentValue)) throw new EraRuntimeException($"Unable to parse alignment type: '{alignmentType}'"); runtime.Console.Alignment = alignmentValue; } private static Color ParseColorArguments(EraRuntime runtime, StackFrame context, ExecutionNode node) { if (node.SubNodes.Length == 1) { uint argb = (uint)runtime.ComputeExpression(context, node[0]).Real; argb |= 0xFF000000U; return Color.FromArgb((int)argb); } else if (node.SubNodes.Length == 3) { int r = (int)runtime.ComputeExpression(context, node[0]).Real; int g = (int)runtime.ComputeExpression(context, node[1]).Real; int b = (int)runtime.ComputeExpression(context, node[2]).Real; return Color.FromArgb(r, g, b); } else throw new EraRuntimeException("Unable to parse color"); } [Keyword("SETCOLOR")] public static void SetColor(EraRuntime runtime, StackFrame context, ExecutionNode node) { runtime.Console.SetStringStyle(ParseColorArguments(runtime, context, node)); } [Keyword("RESETCOLOR")] public static void ResetColor(EraRuntime runtime, StackFrame context, ExecutionNode node) { runtime.Console.ResetStyle(); } [Keyword("SETBGCOLOR")] public static void SetBgColor(EraRuntime runtime, StackFrame context, ExecutionNode node) { runtime.Console.SetBgColor(ParseColorArguments(runtime, context, node)); } [Keyword("RESETBGCOLOR")] public static void ResetBgColor(EraRuntime runtime, StackFrame context, ExecutionNode node) { runtime.Console.SetBgColor(Color.Black); } [Keyword("CLEARLINE")] public static void ClearLine(EraRuntime runtime, StackFrame context, ExecutionNode node) { runtime.Console.ClearLine((int)runtime.ComputeExpression(context, node.Single()).Real); } [Keyword("REDRAW")] public static void Redraw(EraRuntime runtime, StackFrame context, ExecutionNode node) { runtime.Console.SetRedraw((long)runtime.ComputeExpression(context, node.Single()).Real); } [Keyword("DRAWLINE")] public static void DrawLine(EraRuntime runtime, StackFrame context, ExecutionNode node) { runtime.Console.PrintBar(); } [Keyword("CUSTOMDRAWLINE", true)] public static void CustomDrawLine(EraRuntime runtime, StackFrame context, ExecutionNode node) { if (node.SubNodes.Length == 1) { var value = runtime.ComputeExpression(context, node.Single()); runtime.Console.printCustomBar(value.ToString()); } else { throw new ArgumentException("Missing argument to CUSTOMDRAWLINE"); } } [Keyword("DRAWLINEFORM", true, true)] public static void DrawLineForm(EraRuntime runtime, StackFrame context, ExecutionNode node) { var value = runtime.ComputeExpression(context, node.Single()); runtime.Console.printCustomBar(value.ToString()); } [Keyword("PRINT_IMG")] public static void PrintImg(EraRuntime runtime, StackFrame context, ExecutionNode node) { var value = runtime.ComputeExpression(context, node.Single()); runtime.Console.PrintImg(value.ToString()); } [Keyword("PRINTBUTTON")] public static void PrintButton(EraRuntime runtime, StackFrame context, ExecutionNode node) { var textValue = runtime.ComputeExpression(context, node[0]); var intValue = runtime.ComputeExpression(context, node[1]); runtime.Console.PrintButton(textValue.String, (long)intValue.Real); } [Keyword("HTML_PRINT")] public static void PrintHtml(EraRuntime runtime, StackFrame context, ExecutionNode node) { var htmlValue = runtime.ComputeExpression(context, node.Single()); runtime.Console.PrintHtml(htmlValue.String, false); } private static PrintFlags ParsePrintFlags(string opts) { return new PrintFlags { NewLine = opts.Contains("L") || opts.Contains("W"), Wait = opts.Contains("W"), ForceKana = opts.Contains("K"), NoColor = opts.Contains("D") }; } [Keyword("PRINTFORM", true, true)] [Keyword("PRINTFORMK", true, true)] [Keyword("PRINTFORMD", true, true)] [Keyword("PRINTFORML", true, true)] [Keyword("PRINTFORMW", true, true)] [Keyword("PRINTFORMK", true, true)] [Keyword("PRINTFORMKL", true, true)] [Keyword("PRINTFORMKW", true, true)] [Keyword("PRINTFORMD", true, true)] [Keyword("PRINTFORMDL", true, true)] [Keyword("PRINTFORMDW", true, true)] public static void PrintForm(EraRuntime runtime, StackFrame context, ExecutionNode node) { var flags = ParsePrintFlags(node["name"].Substring(9)); var value = runtime.ComputeExpression(context, node.Single()); runtime.Console.Write(value.ToString(), flags); if (flags.Wait) { runtime.InputResetEvent.WaitOne(); } } [Keyword("PRINT", true, false)] [Keyword("PRINTL", true, false)] [Keyword("PRINTW", true, false)] [Keyword("PRINTK", true, false)] [Keyword("PRINTKL", true, false)] [Keyword("PRINTKW", true, false)] [Keyword("PRINTD", true, false)] [Keyword("PRINTDL", true, false)] [Keyword("PRINTDW", true, false)] public static void Print(EraRuntime runtime, StackFrame context, ExecutionNode node) { var flags = ParsePrintFlags(node["name"].Substring(5)); var value = runtime.ComputeExpression(context, node.Single()); runtime.Console.Write(value.ToString(), flags); if (flags.Wait) { runtime.InputResetEvent.WaitOne(); } } private class PrintDataOptions { public PrintFlags Flags = new PrintFlags(); public int Count = 0; public ExecutionNode ChosenNode; } static private PrintDataOptions CurrentPrintDataOptions; [Keyword("PRINTDATA")] [Keyword("PRINTDATAL")] [Keyword("PRINTDATAW")] [Keyword("PRINTDATAK")] [Keyword("PRINTDATAKL")] [Keyword("PRINTDATAKW")] [Keyword("PRINTDATAD")] [Keyword("PRINTDATADL")] [Keyword("PRINTDATADW")] public static void PrintData(EraRuntime runtime, StackFrame context, ExecutionNode node) { /* This function doesn't do anything itself, but lets you specify a bunch of DATAFORMs followed by a ENDDATA and it picks one at random and prints that. */ CurrentPrintDataOptions = new PrintDataOptions(); CurrentPrintDataOptions.Flags = ParsePrintFlags(node["name"].Substring(9)); } [Keyword("DATA", true, true)] [Keyword("DATAFORM", true, true)] public static void DataForm(EraRuntime runtime, StackFrame context, ExecutionNode node) { CurrentPrintDataOptions.Count++; if (random.Next(0, CurrentPrintDataOptions.Count) == 0) CurrentPrintDataOptions.ChosenNode = node.Single(); } [Keyword("ENDDATA")] public static void EndData(EraRuntime runtime, StackFrame context, ExecutionNode node) { if (CurrentPrintDataOptions.ChosenNode == null) { throw new ArgumentException("No DATAFORM found before ENDDATA"); } var value = runtime.ComputeExpression(context, CurrentPrintDataOptions.ChosenNode); var flags = CurrentPrintDataOptions.Flags; runtime.Console.Write(value.ToString(), flags); if (flags.Wait) { runtime.InputResetEvent.WaitOne(); } } } }