EraRuntime.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using NTERA.Core;
  5. using NTERA.EmuEra.Game.EraEmu.Content;
  6. using NTERA.Engine.Compiler;
  7. namespace NTERA.Engine.Runtime
  8. {
  9. public class EraRuntime : IScriptEngine
  10. {
  11. public IExecutionProvider ExecutionProvider { get; }
  12. public IConsole Console { get; protected set; }
  13. public Stack<StackFrame> ExecutionStack { get; } = new Stack<StackFrame>();
  14. protected List<FunctionDefinition> TotalProcedureDefinitions = new List<FunctionDefinition>(BaseDefinitions.DefaultGlobalFunctions);
  15. public EraRuntime(IExecutionProvider executionProvider)
  16. {
  17. ExecutionProvider = executionProvider;
  18. TotalProcedureDefinitions.AddRange(ExecutionProvider.DefinedProcedures);
  19. TotalProcedureDefinitions.AddRange(BaseDefinitions.DefaultGlobalFunctions);
  20. }
  21. public bool Initialize(IConsole console)
  22. {
  23. Console = console;
  24. return true;
  25. }
  26. public void Start()
  27. {
  28. Console.PrintSystemLine("EraJIT x64 0.0.0.0");
  29. Console.PrintSystemLine("");
  30. try
  31. {
  32. Call(ExecutionProvider.DefinedProcedures.First(x => x.Name == "SYSTEM_TITLE"));
  33. }
  34. catch (Exception ex)
  35. {
  36. Console.PrintSystemLine($"Unhandled exception: {ex.Message}");
  37. Console.PrintSystemLine("Stack trace:");
  38. foreach (var stackMember in ExecutionStack)
  39. Console.PrintSystemLine($" - @{stackMember.SelfDefinition.Name} ({stackMember.SelfDefinition.Position} > {stackMember.SelfDefinition.Filename})");
  40. throw;
  41. }
  42. System.Threading.Thread.Sleep(-1);
  43. }
  44. public ExecutionResult Call(FunctionDefinition function, IList<Value> parameters = null)
  45. {
  46. var localVariables = new VariableDictionary();
  47. foreach (var variable in function.Variables)
  48. localVariables[variable.Name] = variable.CalculatedValue;
  49. var globalVariables = new VariableDictionary();
  50. foreach (var variable in BaseDefinitions.DefaultGlobalVariables)
  51. globalVariables[variable.Name] = variable.CalculatedValue;
  52. var context = new StackFrame
  53. {
  54. SelfDefinition = function,
  55. GlobalVariables = globalVariables,
  56. LocalVariables = localVariables
  57. };
  58. ExecutionResult result;
  59. ExecutionStack.Push(context);
  60. if (function.Filename == "__GLOBAL")
  61. {
  62. var resultValue = Base.Functions.StaticFunctions[function.Name].Invoke(this, context, parameters);
  63. result = new ExecutionResult(ExecutionResultType.FunctionReturn, resultValue);
  64. }
  65. else
  66. {
  67. if (parameters != null)
  68. for (var index = 0; index < parameters.Count; index++)
  69. {
  70. FunctionParameter parameter;
  71. if (index < function.Parameters.Length)
  72. parameter = function.Parameters[index];
  73. else if (index >= function.Parameters.Length && function.Parameters.Last().IsArrayParameter)
  74. parameter = function.Parameters.Last();
  75. else
  76. throw new Exception($"Unable to assign parameter #{index + 1}");
  77. if (localVariables.ContainsKey(parameter.Name))
  78. localVariables[parameter.Name] = parameters[index];
  79. else if (globalVariables.ContainsKey(parameter.Name))
  80. globalVariables[parameter.Name] = parameters[index];
  81. else
  82. throw new Exception($"Unable to assign parameter '{parameter.Name}' to a variable");
  83. }
  84. var executionContents = ExecutionProvider.GetExecutionNodes(function);
  85. result = ExecuteSet(context, executionContents.ToList());
  86. }
  87. ExecutionStack.Pop();
  88. return result;
  89. }
  90. public ExecutionResult ExecuteSet(StackFrame context, IList<ExecutionNode> nodes)
  91. {
  92. for (int index = 0; index < nodes.Count; index++)
  93. {
  94. ExecutionNode node = nodes[index];
  95. if (node.Type == "result")
  96. {
  97. return new ExecutionResult(ExecutionResultType.FunctionReturn, ComputeExpression(context, node.SubNodes.Single()));
  98. }
  99. ExecuteNode(context, node);
  100. }
  101. return ExecutionResult.None;
  102. }
  103. public void ExecuteNode(StackFrame context, ExecutionNode node)
  104. {
  105. switch (node.Type)
  106. {
  107. case "statement":
  108. string statement = node.Metadata["name"];
  109. if (!Base.Keywords.StaticKeywords.TryGetValue(statement, out var keywordAction))
  110. throw new Exception($"Unknown statement: '{statement}'");
  111. keywordAction(this, context, node);
  112. return;
  113. case "assignment":
  114. string variableName = node["variable"].Metadata["name"];
  115. if (context.LocalVariables.ContainsKey(variableName))
  116. context.LocalVariables[variableName] = ComputeExpression(context, node["value"].SubNodes.Single());
  117. else if (context.GlobalVariables.ContainsKey(variableName))
  118. context.GlobalVariables[variableName] = ComputeExpression(context, node["value"].SubNodes.Single());
  119. else
  120. throw new Exception($"Unknown variable: '{variableName}'");
  121. return;
  122. default:
  123. throw new Exception($"Unknown node type: '{node.Type}'");
  124. }
  125. }
  126. public Value ComputeExpression(StackFrame context, ExecutionNode expressionNode)
  127. {
  128. switch (expressionNode.Type)
  129. {
  130. case "constant":
  131. ValueType type = (ValueType)Enum.Parse(typeof(ValueType), expressionNode.Metadata["type"]);
  132. string strValue = expressionNode.Metadata["value"];
  133. return type == ValueType.String ? (Value)strValue : (Value)double.Parse(strValue);
  134. case "variable":
  135. string variableName = expressionNode.Metadata["name"];
  136. if (context.LocalVariables.ContainsKey(variableName))
  137. return context.LocalVariables[variableName];
  138. else if (context.GlobalVariables.ContainsKey(variableName))
  139. return context.GlobalVariables[variableName];
  140. else
  141. throw new Exception($"Unable to retrieve variable '{variableName}'");
  142. case "call":
  143. string functionName = expressionNode.Metadata["target"];
  144. var function = TotalProcedureDefinitions.FirstOrDefault(func => func.IsReturnFunction && func.Name.Equals(functionName, StringComparison.OrdinalIgnoreCase));
  145. if (function == null)
  146. throw new Exception($"Unknown function: '{functionName}'");
  147. var executionResult = Call(function, expressionNode["parameters"].SubNodes.Select(x => ComputeExpression(context, x)).ToArray());
  148. if (executionResult.Type != ExecutionResultType.FunctionReturn || !executionResult.Result.HasValue)
  149. throw new Exception($"Unexpected result from function '{functionName}': {executionResult.Type}");
  150. return executionResult.Result.Value;
  151. case "operation":
  152. bool isUnary = expressionNode.Metadata.ContainsKey("unary") && bool.Parse(expressionNode.Metadata["unary"]);
  153. string operationType = expressionNode.Metadata["type"];
  154. Token operatorToken;
  155. switch (operationType)
  156. {
  157. case "add": operatorToken = Token.Plus; break;
  158. case "subtract": operatorToken = Token.Minus; break;
  159. default: throw new Exception($"Unknown operation type: '{operationType}'");
  160. }
  161. if (isUnary)
  162. {
  163. Value innerValue = ComputeExpression(context, expressionNode.SubNodes.Single());
  164. switch (operatorToken)
  165. {
  166. case Token.Plus: return innerValue;
  167. case Token.Minus: return innerValue * -1;
  168. default: throw new Exception($"Unsupported unary operation type: '{operationType}'");
  169. }
  170. }
  171. var left = ComputeExpression(context, expressionNode.SubNodes[0]);
  172. var right = ComputeExpression(context, expressionNode.SubNodes[1]);
  173. return left.Operate(right, operatorToken);
  174. default:
  175. throw new Exception($"Unknown expression type: '{expressionNode.Type}'");
  176. }
  177. }
  178. public void InputString(string input)
  179. {
  180. throw new NotImplementedException();
  181. }
  182. public void InputInteger(long input)
  183. {
  184. throw new NotImplementedException();
  185. }
  186. public void InputSystemInteger(long input)
  187. {
  188. throw new NotImplementedException();
  189. }
  190. public CroppedImage GetImage(string name)
  191. {
  192. throw new NotImplementedException();
  193. }
  194. }
  195. }