EraRuntime.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Linq;
  5. using System.Runtime.CompilerServices;
  6. using NTERA.Core;
  7. using NTERA.EmuEra.Game.EraEmu.Content;
  8. using NTERA.Engine.Compiler;
  9. namespace NTERA.Engine.Runtime
  10. {
  11. public class EraRuntime : IScriptEngine
  12. {
  13. public IExecutionProvider ExecutionProvider { get; }
  14. public IConsole Console { get; protected set; }
  15. public Stack<StackFrame> ExecutionStack { get; } = new Stack<StackFrame>();
  16. public List<FunctionDefinition> TotalProcedureDefinitions { get; set; } = new List<FunctionDefinition>(BaseDefinitions.DefaultGlobalFunctions);
  17. public Dictionary<string, Variable> GlobalVariables { get; set; } = new Dictionary<string, Variable>();
  18. public Dictionary<string, Variable> SemiGlobalVariables { get; set; } = new Dictionary<string, Variable>();
  19. public EraRuntime(IExecutionProvider executionProvider)
  20. {
  21. ExecutionProvider = executionProvider;
  22. TotalProcedureDefinitions.AddRange(ExecutionProvider.DefinedProcedures);
  23. TotalProcedureDefinitions.AddRange(BaseDefinitions.DefaultGlobalFunctions);
  24. foreach (var variable in BaseDefinitions.DefaultGlobalVariables)
  25. {
  26. var globalVariable = new Variable(variable.Name, variable.ValueType);
  27. globalVariable[0] = variable.CalculatedValue;
  28. GlobalVariables.Add(variable.Name, globalVariable);
  29. }
  30. }
  31. public bool Initialize(IConsole console)
  32. {
  33. Console = console;
  34. return true;
  35. }
  36. public void Start()
  37. {
  38. Console.PrintSystemLine("EraJIT x64 0.0.0.0");
  39. Console.PrintSystemLine("");
  40. try
  41. {
  42. Call(ExecutionProvider.DefinedProcedures.First(x => x.Name == "SYSTEM_TITLE"));
  43. }
  44. catch (Exception ex)
  45. {
  46. Console.PrintSystemLine($"Unhandled exception: {ex.Message}");
  47. Console.PrintSystemLine("Stack trace:");
  48. foreach (var stackMember in ExecutionStack)
  49. Console.PrintSystemLine($" - @{stackMember.SelfDefinition.Name} ({stackMember.SelfDefinition.Position} > {stackMember.SelfDefinition.Filename})");
  50. throw;
  51. }
  52. System.Threading.Thread.Sleep(-1);
  53. }
  54. public ExecutionResult Call(FunctionDefinition function, IList<Parameter> parameters = null)
  55. {
  56. var localVariables = new Dictionary<string, Variable>();
  57. foreach (var variable in function.Variables)
  58. {
  59. var localVariable = new Variable(variable.Name, variable.ValueType);
  60. localVariable[0] = variable.CalculatedValue;
  61. localVariables.Add(variable.Name, localVariable);
  62. }
  63. foreach (var variable in GlobalVariables)
  64. {
  65. localVariables.Add(variable.Key, variable.Value);
  66. }
  67. var newContext = new StackFrame
  68. {
  69. SelfDefinition = function,
  70. Variables = localVariables
  71. };
  72. ExecutionResult result;
  73. ExecutionStack.Push(newContext);
  74. if (function.Filename == "__GLOBAL")
  75. {
  76. var resultValue = Base.Functions.StaticFunctions[function.Name].Invoke(this, newContext, parameters);
  77. result = new ExecutionResult(ExecutionResultType.FunctionReturn, resultValue);
  78. }
  79. else
  80. {
  81. if (parameters != null)
  82. {
  83. for (var index = 0; index < parameters.Count; index++)
  84. {
  85. FunctionParameter parameter;
  86. if (index < function.Parameters.Length)
  87. parameter = function.Parameters[index];
  88. else if (index >= function.Parameters.Length && function.Parameters.Last().IsArrayParameter)
  89. parameter = function.Parameters.Last();
  90. else
  91. throw new EraRuntimeException($"Unable to assign parameter #{index + 1}");
  92. var localVariable = function.Variables.FirstOrDefault(x => x.Name == parameter.Name);
  93. if (localVariable != null && localVariable.VariableType.HasFlag(VariableType.Reference))
  94. {
  95. if (parameters[index].BackingVariable == null)
  96. throw new EraRuntimeException("Expected a variable to pass through as REF");
  97. newContext.Variables[localVariable.Name] = parameters[index].BackingVariable;
  98. }
  99. else
  100. {
  101. var paramVariable = ComputeVariable(newContext, parameter.Name);
  102. paramVariable[parameter.Index] = parameters[index];
  103. }
  104. }
  105. }
  106. var executionContents = ExecutionProvider.GetExecutionNodes(function);
  107. result = ExecuteSet(newContext, executionContents.ToList());
  108. }
  109. ExecutionStack.Pop();
  110. return result;
  111. }
  112. public ExecutionResult ExecuteSet(StackFrame context, IList<ExecutionNode> nodes)
  113. {
  114. for (int index = 0; index < nodes.Count; index++)
  115. {
  116. ExecutionNode node = nodes[index];
  117. if (node.Type == "for")
  118. {
  119. ExecutionNode forContext = node[0];
  120. var iterationVariable = ComputeVariable(context, forContext[0], out var iterationIndex);
  121. var beginNumber = ComputeExpression(context, forContext[1]);
  122. var endNumber = ComputeExpression(context, forContext[2]);
  123. for (iterationVariable[iterationIndex] = beginNumber.Real; iterationVariable[iterationIndex].Real < endNumber.Real; iterationVariable[iterationIndex]++)
  124. {
  125. var subset = node.Skip(1).ToList();
  126. ExecuteSet(context, subset);
  127. }
  128. continue;
  129. }
  130. if (node.Type == "result")
  131. {
  132. return new ExecutionResult(ExecutionResultType.FunctionReturn, ComputeExpression(context, node.Single()));
  133. }
  134. ExecuteNode(context, node);
  135. }
  136. return ExecutionResult.None;
  137. }
  138. public void ExecuteNode(StackFrame context, ExecutionNode node)
  139. {
  140. switch (node.Type)
  141. {
  142. case "statement":
  143. string statement = node["name"];
  144. if (!Base.Keywords.StaticKeywords.TryGetValue(statement, out var keywordAction))
  145. throw new EraRuntimeException($"Unknown statement: '{statement}'");
  146. keywordAction(this, context, node);
  147. return;
  148. case "assignment":
  149. Variable variable = ComputeVariable(context, node.GetSubtype("variable"), out var index);
  150. variable[index] = ComputeExpression(context, node.GetSubtype("value").Single());
  151. return;
  152. case "call":
  153. string procedureName = node["target"];
  154. var procedure = TotalProcedureDefinitions.FirstOrDefault(func => !func.IsReturnFunction && func.Name.Equals(procedureName, StringComparison.OrdinalIgnoreCase));
  155. if (procedure == null)
  156. throw new EraRuntimeException($"Unknown procedure: '{procedureName}'");
  157. var executionResult = Call(procedure, node.GetSubtype("parameters").Select(x => ComputeParameter(context, x)).ToArray());
  158. if (executionResult.Type != ExecutionResultType.None || executionResult.Result.HasValue)
  159. throw new EraRuntimeException($"Unexpected result from procedure '{procedureName}': {executionResult.Type}");
  160. return;
  161. default:
  162. throw new EraRuntimeException($"Unknown node type: '{node.Type}'");
  163. }
  164. }
  165. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  166. public Variable ComputeVariable(StackFrame context, ExecutionNode variableNode, out int[] index)
  167. {
  168. string variableName = variableNode["name"];
  169. index = new[] { 0 };
  170. if (variableNode.SubNodes.Any(x => x.Type == "index"))
  171. {
  172. ExecutionNode indexNode = variableNode.GetSubtype("index");
  173. index = indexNode.SubNodes.Select(x => (int)ComputeExpression(context, x)).ToArray();
  174. }
  175. return ComputeVariable(context, variableName);
  176. }
  177. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  178. public Variable ComputeVariable(StackFrame context, string variableName)
  179. {
  180. if (context.Variables.TryGetValue(variableName, out var variable))
  181. return variable;
  182. throw new EraRuntimeException($"Unable to retrieve variable '{variableName}'");
  183. }
  184. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  185. public Parameter ComputeParameter(StackFrame context, ExecutionNode variableNode)
  186. {
  187. if (variableNode.Type == "variable")
  188. {
  189. var variable = ComputeVariable(context, variableNode, out int[] index);
  190. return new Parameter(variable[index], variable, index);
  191. }
  192. return new Parameter(ComputeExpression(context, variableNode));
  193. }
  194. public Value ComputeExpression(StackFrame context, ExecutionNode expressionNode)
  195. {
  196. switch (expressionNode.Type)
  197. {
  198. case "constant":
  199. ValueType type = (ValueType)Enum.Parse(typeof(ValueType), expressionNode["type"]);
  200. string strValue = expressionNode["value"];
  201. return type == ValueType.String ? (Value)strValue : (Value)double.Parse(strValue);
  202. case "variable":
  203. Variable variable = ComputeVariable(context, expressionNode, out var index);
  204. return variable[index];
  205. case "call":
  206. string functionName = expressionNode["target"];
  207. var function = TotalProcedureDefinitions.FirstOrDefault(func => func.IsReturnFunction && func.Name.Equals(functionName, StringComparison.OrdinalIgnoreCase));
  208. if (function == null)
  209. throw new EraRuntimeException($"Unknown function: '{functionName}'");
  210. var executionResult = Call(function, expressionNode.GetSubtype("parameters").Select(x => ComputeParameter(context, x)).ToArray());
  211. if (executionResult.Type != ExecutionResultType.FunctionReturn || !executionResult.Result.HasValue)
  212. throw new EraRuntimeException($"Unexpected result from function '{functionName}': {executionResult.Type}");
  213. return executionResult.Result.Value;
  214. case "operation":
  215. bool isUnary = expressionNode.Metadata.ContainsKey("unary") && bool.Parse(expressionNode["unary"]);
  216. string operationType = expressionNode["type"];
  217. Token operatorToken;
  218. switch (operationType)
  219. {
  220. case "add":
  221. operatorToken = Token.Plus;
  222. break;
  223. case "subtract":
  224. operatorToken = Token.Minus;
  225. break;
  226. case "multiply":
  227. operatorToken = Token.Asterisk;
  228. break;
  229. default: throw new EraRuntimeException($"Unknown operation type: '{operationType}'");
  230. }
  231. if (isUnary)
  232. {
  233. Value innerValue = ComputeExpression(context, expressionNode.Single());
  234. switch (operatorToken)
  235. {
  236. case Token.Plus: return innerValue;
  237. case Token.Minus: return innerValue * -1;
  238. default: throw new EraRuntimeException($"Unsupported unary operation type: '{operationType}'");
  239. }
  240. }
  241. var left = ComputeExpression(context, expressionNode[0]);
  242. var right = ComputeExpression(context, expressionNode[1]);
  243. return left.Operate(right, operatorToken);
  244. default:
  245. throw new EraRuntimeException($"Unknown expression type: '{expressionNode.Type}'");
  246. }
  247. }
  248. public void InputString(string input)
  249. {
  250. throw new NotImplementedException();
  251. }
  252. public void InputInteger(long input)
  253. {
  254. throw new NotImplementedException();
  255. }
  256. public void InputSystemInteger(long input)
  257. {
  258. throw new NotImplementedException();
  259. }
  260. public CroppedImage GetImage(string name)
  261. {
  262. var bitmap = new Bitmap(@"M:\era\eraSemifullTest\resources\bbb.png");
  263. return new CroppedImage(name, bitmap, new Rectangle(Point.Empty, bitmap.Size), false);
  264. }
  265. }
  266. }