Browse Source

Implement initial runtime engine and JIT

Bepsi 6 years ago
parent
commit
bd82ea182e

+ 2 - 11
NTERA.Compiler/Compiler.cs

@@ -30,11 +30,6 @@ namespace NTERA.Compiler
 
 		public void Compile(string outputDirectory)
 		{
-			var globals = new VariableDictionary();
-
-			foreach (var variable in BaseDefinitions.DefaultGlobalVariables)
-				globals.Add(variable.Name, variable.CalculatedValue);
-
 			string csvPath = Path.Combine(InputDirectory, "CSV");
 			string erbPath = Path.Combine(InputDirectory, "ERB");
 
@@ -122,7 +117,7 @@ namespace NTERA.Compiler
 			Console.WriteLine("Compiling functions...");
 
 #if DEBUG
-			foreach (var kv in DeclaredFunctions)
+			foreach (var kv in DeclaredProcedures)
 #else
 			Parallel.ForEach(DeclaredProcedures, new ParallelOptions
 			{
@@ -132,11 +127,7 @@ namespace NTERA.Compiler
 			{
 				try
 				{
-					var locals = new VariableDictionary();
-					foreach (var param in kv.Key.Variables)
-						locals[param.Name] = param.CalculatedValue;
-
-					Parser parser = new Parser(kv.Value, kv.Key, declaredFunctions, procedures, globals, locals, BaseDefinitions.DefaultKeywords, csvDefinition, preprocessedConstants.ToArray());
+					Parser parser = new Parser(kv.Value, kv.Key, declaredFunctions, procedures, BaseDefinitions.DefaultGlobalVariables, kv.Key.Variables, BaseDefinitions.DefaultKeywords, csvDefinition, preprocessedConstants.ToArray());
 
 					var nodes = parser.Parse(out var localErrors, out var localWarnings);
 

+ 25 - 25
NTERA.Engine/Compiler/Parser.cs

@@ -14,23 +14,22 @@ namespace NTERA.Engine.Compiler
 		protected ICollection<FunctionDefinition> FunctionDefinitions { get; }
 		protected ICollection<FunctionDefinition> ProcedureDefinitions { get; }
 		protected ICollection<FunctionVariable> ConstantDefinitions { get; }
+		protected ICollection<FunctionVariable> GlobalVariables { get; }
+		protected ICollection<FunctionVariable> LocalVariables { get; }
+		protected ICollection<Keyword> ExplicitKeywords { get; }
 
-		protected List<ParserError> Errors { get; } = new List<ParserError>();
-		protected List<ParserError> Warnings { get; } = new List<ParserError>();
-
-		protected VariableDictionary GlobalVariables { get; }
+		protected CSVDefinition CsvDefinition { get; }
 
-		protected VariableDictionary LocalVariables { get; }
 
-		protected ICollection<Keyword> ExplicitKeywords { get; }
+		protected List<ParserError> Errors { get; } = new List<ParserError>();
+		protected List<ParserError> Warnings { get; } = new List<ParserError>();
 
-		protected CSVDefinition CsvDefinition { get; }
 
 		protected IEnumerator<Token> Enumerator { get; }
 
 		protected bool hasPeeked = false;
 		protected Token peekedToken = Token.Unknown;
-
+		
 		protected Token GetNextToken(bool peek = false)
 		{
 			if (peek && hasPeeked)
@@ -49,7 +48,7 @@ namespace NTERA.Engine.Compiler
 			Lexer.TokenMarker.Line + SelfDefinition.Position.Line - 1,
 			Lexer.TokenMarker.Column);
 
-		public Parser(string input, FunctionDefinition selfDefinition, ICollection<FunctionDefinition> functionDefinitions, ICollection<FunctionDefinition> procedureDefinitions, VariableDictionary globalVariables, VariableDictionary localVariables, ICollection<Keyword> explicitKeywords, CSVDefinition csvDefinition, ICollection<FunctionVariable> constantDefnitions)
+		public Parser(string input, FunctionDefinition selfDefinition, ICollection<FunctionDefinition> functionDefinitions, ICollection<FunctionDefinition> procedureDefinitions, ICollection<FunctionVariable> globalVariables, ICollection<FunctionVariable> localVariables, ICollection<Keyword> explicitKeywords, CSVDefinition csvDefinition, ICollection<FunctionVariable> constantDefinitions)
 		{
 			Lexer = new Lexer(input);
 			Enumerator = Lexer.GetEnumerator();
@@ -57,7 +56,7 @@ namespace NTERA.Engine.Compiler
 			SelfDefinition = selfDefinition;
 			FunctionDefinitions = functionDefinitions;
 			ProcedureDefinitions = procedureDefinitions;
-			ConstantDefinitions = constantDefnitions;
+			ConstantDefinitions = constantDefinitions;
 			GlobalVariables = globalVariables;
 			LocalVariables = localVariables;
 			ExplicitKeywords = explicitKeywords;
@@ -118,18 +117,16 @@ namespace NTERA.Engine.Compiler
 			{
 				case Token.Identifer:
 
-					if (GlobalVariables.ContainsKey(Lexer.Identifier)
-						|| LocalVariables.ContainsKey(Lexer.Identifier)
-						|| ConstantDefinitions.Any(x => x.Name.Equals(Lexer.Identifier, StringComparison.OrdinalIgnoreCase)))
+					if (IsVariable(Lexer.Identifier))
 					{
 						string variableName = Lexer.Identifier;
 
-						ValueType type = (ValueType)0;
-
-						if (GlobalVariables.ContainsKey(variableName))
-							type = GlobalVariables[variableName].Type;
-						else if (LocalVariables.ContainsKey(variableName))
-							type = LocalVariables[variableName].Type;
+						ValueType type = 0;
+						
+						if (GlobalVariables.Any(x => x.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)))
+							type = GlobalVariables.First(x => x.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)).ValueType;
+						else if (LocalVariables.Any(x => x.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)))
+							type = LocalVariables.First(x => x.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)).ValueType;
 						else if (ConstantDefinitions.Any(x => x.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)))
 							type = ConstantDefinitions.First(x => x.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)).ValueType;
 
@@ -514,6 +511,13 @@ namespace NTERA.Engine.Compiler
 			}
 		}
 
+		protected bool IsVariable(string identifier)
+		{
+			return GlobalVariables.Any(x => x.Name.Equals(identifier, StringComparison.OrdinalIgnoreCase))
+				   || LocalVariables.Any(x => x.Name.Equals(identifier, StringComparison.OrdinalIgnoreCase))
+				   || ConstantDefinitions.Any(x => x.Name.Equals(identifier, StringComparison.OrdinalIgnoreCase));
+		}
+
 		protected ExecutionNode GetVariable(out ParserError error)
 		{
 			string variableName = Lexer.Identifier;
@@ -562,9 +566,7 @@ namespace NTERA.Engine.Compiler
 						continue;
 					}
 
-					if (GlobalVariables.ContainsKey(Lexer.Identifier)
-						|| LocalVariables.ContainsKey(Lexer.Identifier)
-						|| ConstantDefinitions.Any(x => x.Name == Lexer.Identifier))
+					if (IsVariable(Lexer.Identifier))
 					{
 						var subNode = new ExecutionNode
 						{
@@ -783,9 +785,7 @@ namespace NTERA.Engine.Compiler
 				}
 				else if (token == Token.Identifer)
 				{
-					if (GlobalVariables.ContainsKey(Lexer.Identifier)
-						|| LocalVariables.ContainsKey(Lexer.Identifier)
-						|| ConstantDefinitions.Any(x => x.Name == Lexer.Identifier))
+					if (IsVariable(Lexer.Identifier))
 					{
 						operands.Push(GetVariable(out error));
 						if (error != null)

+ 4 - 0
NTERA.Engine/NTERA.Engine.csproj

@@ -52,7 +52,11 @@
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Compiler\Token.cs" />
     <Compile Include="Runtime\BaseDefinitions.cs" />
+    <Compile Include="Runtime\Base\Keywords.cs" />
     <Compile Include="Runtime\EraRuntime.cs" />
+    <Compile Include="Runtime\ExecutionSet.cs" />
+    <Compile Include="Runtime\IExecutionProvider.cs" />
+    <Compile Include="Runtime\JITCompiler.cs" />
     <Compile Include="Utility.cs" />
     <Compile Include="Value.cs" />
     <Compile Include="Variable.cs" />

+ 16 - 0
NTERA.Engine/Runtime/Base/Keywords.cs

@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace NTERA.Engine.Runtime.Base
+{
+	public static class Keywords
+	{
+		public static void DrawLine(EraRuntime runtime, ExecutionSet set)
+		{
+			runtime.Console.PrintBar();
+		}
+	}
+}

+ 91 - 3
NTERA.Engine/Runtime/EraRuntime.cs

@@ -1,12 +1,100 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using NTERA.Core;
+using NTERA.EmuEra.Game.EraEmu.Content;
+using NTERA.Engine.Compiler;
 
 namespace NTERA.Engine.Runtime
 {
-	public class EraRuntime
+	public class EraRuntime : IScriptEngine
 	{
+		public IExecutionProvider ExecutionProvider { get; }
+		public IConsole Console { get; protected set; }
+
+		public Stack<ExecutionSet> ExecutionStack { get; } = new Stack<ExecutionSet>();
+
+		public EraRuntime(IExecutionProvider executionProvider)
+		{
+			ExecutionProvider = executionProvider;
+		}
+
+		public bool Initialize(IConsole console)
+		{
+			Console = console;
+			return true;
+		}
+
+		public void Start()
+		{
+			Console.PrintSystemLine("EraJIT x64 0.0.0.0");
+			Console.PrintSystemLine("");
+
+			Call(ExecutionProvider.DefinedProcedures.First(x => x.Name == "SYSTEM_TITLE"));
+
+			System.Threading.Thread.Sleep(-1);
+		}
+
+		public void Call(FunctionDefinition function)
+		{
+			var executionContents = ExecutionProvider.GetExecutionNodes(function);
+
+			var executionSet = new ExecutionSet
+			{
+				Nodes = executionContents.ToList(),
+				SelfDefinition = function
+			};
+
+			ExecutionStack.Push(executionSet);
+
+			ExecuteSet(executionSet);
+
+			ExecutionStack.Pop();
+		}
+
+		protected void ExecuteSet(ExecutionSet set)
+		{
+			for (; set.Position < set.Nodes.Count; set.Position++)
+			{
+				ExecutionNode node = set.Nodes[set.Position];
+
+				switch (node.Type)
+				{
+					case "statement":
+						string statement = node.Metadata["name"];
+
+						if (statement == "DRAWLINE")
+							Base.Keywords.DrawLine(this, set);
+						else
+						{
+							throw new Exception($"Unknown statement: '{statement}'");
+						}
+
+						break;
+					default:
+						throw new Exception($"Unknown node type: '{node.Type}'");
+				}
+			}
+		}
+
+		public void InputString(string input)
+		{
+			throw new NotImplementedException();
+		}
+
+		public void InputInteger(long input)
+		{
+			throw new NotImplementedException();
+		}
+
+		public void InputSystemInteger(long input)
+		{
+			throw new NotImplementedException();
+		}
+
+		public CroppedImage GetImage(string name)
+		{
+			throw new NotImplementedException();
+		}
 	}
 }

+ 14 - 0
NTERA.Engine/Runtime/ExecutionSet.cs

@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+using NTERA.Engine.Compiler;
+
+namespace NTERA.Engine.Runtime
+{
+	public class ExecutionSet
+	{
+		public FunctionDefinition SelfDefinition { get; set; }
+
+		public IList<ExecutionNode> Nodes { get; set; }
+
+		public int Position { get; set; } = 0;
+	}
+}

+ 15 - 0
NTERA.Engine/Runtime/IExecutionProvider.cs

@@ -0,0 +1,15 @@
+using System.Collections.Generic;
+using NTERA.Engine.Compiler;
+
+namespace NTERA.Engine.Runtime
+{
+	public interface IExecutionProvider
+	{
+		ICollection<FunctionDefinition> DefinedProcedures { get; }
+		ICollection<FunctionDefinition> DefinedFunctions { get; }
+		ICollection<FunctionVariable> DefinedConstants { get; }
+		CSVDefinition CSVDefinition { get; }
+
+		IEnumerable<ExecutionNode> GetExecutionNodes(FunctionDefinition function);
+	}
+}

+ 133 - 0
NTERA.Engine/Runtime/JITCompiler.cs

@@ -0,0 +1,133 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NTERA.Engine.Compiler;
+
+namespace NTERA.Engine.Runtime
+{
+	public class JITCompiler : IExecutionProvider
+	{
+		public ICollection<FunctionDefinition> DefinedProcedures { get; protected set; }
+		public ICollection<FunctionDefinition> DefinedFunctions { get; protected set; }
+		public ICollection<FunctionVariable> DefinedConstants { get; protected set; }
+		public CSVDefinition CSVDefinition { get; protected set; }
+
+		public Dictionary<FunctionDefinition, ExecutionNode[]> CompiledProcedures { get; protected set; } = new Dictionary<FunctionDefinition, ExecutionNode[]>();
+
+		public string InputDirectory { get; }
+
+		public static int Threads = 4;
+
+		public JITCompiler(string path)
+		{
+			InputDirectory = path;
+
+			Compile();
+		}
+
+		protected void Compile()
+		{
+			string csvPath = Path.Combine(InputDirectory, "CSV");
+			string erbPath = Path.Combine(InputDirectory, "ERB");
+
+			var csvDefinition = new CSVDefinition();
+
+			Console.WriteLine("Preprocessing CSV files...");
+
+
+			foreach (var file in Directory.EnumerateFiles(csvPath, "*.csv", SearchOption.AllDirectories))
+			{
+				string path = Path.GetFileNameWithoutExtension(file);
+
+				if (path.EndsWith("_TR"))
+					continue;
+
+				Preprocessor.ProcessCSV(csvDefinition, path, File.ReadLines(file, Encoding.UTF8));
+			}
+
+			Console.WriteLine("Preprocessing header files...");
+
+			ConcurrentBag<FunctionVariable> preprocessedConstants = new ConcurrentBag<FunctionVariable>();
+
+#if DEBUG
+			foreach (var file in Directory.EnumerateFiles(erbPath, "*.erh", SearchOption.AllDirectories))
+#else
+			Parallel.ForEach(Directory.EnumerateFiles(erbPath, "*.erh", SearchOption.AllDirectories), new ParallelOptions
+			{
+				MaxDegreeOfParallelism = Threads
+			}, file =>
+#endif
+			{
+				foreach (var variable in Preprocessor.PreprocessHeaderFile(File.ReadAllText(file)))
+					preprocessedConstants.Add(variable);
+#if DEBUG
+			}
+#else
+			});
+#endif
+
+			Console.WriteLine("Preprocessing functions...");
+
+			ConcurrentDictionary<FunctionDefinition, string> preprocessedFunctions = new ConcurrentDictionary<FunctionDefinition, string>();
+
+#if DEBUG
+			foreach (var file in Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories))
+#else
+			Parallel.ForEach(Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories), new ParallelOptions
+			{
+				MaxDegreeOfParallelism = Threads
+			}, file =>
+#endif
+			{
+				foreach (var kv in Preprocessor.PreprocessCodeFile(File.ReadAllText(file), Path.GetFileName(file), preprocessedConstants.ToArray()))
+					preprocessedFunctions[kv.Key] = kv.Value;
+#if DEBUG
+			}
+#else
+			});
+#endif
+			DefinedProcedures = preprocessedFunctions.Select(kv => kv.Key).ToArray();
+			var DeclaredProcedures = preprocessedFunctions.ToDictionary(kv => kv.Key, kv => kv.Value);
+
+			var declaredFunctions = BaseDefinitions.DefaultGlobalFunctions.ToList();
+			declaredFunctions.AddRange(DeclaredProcedures.Keys.Where(x => x.IsReturnFunction));
+
+			var procedures = DeclaredProcedures.Keys.Where(x => !x.IsReturnFunction).ToList();
+
+
+			Console.WriteLine("Compiling functions...");
+
+#if DEBUG
+			foreach (var kv in DeclaredProcedures)
+#else
+			Parallel.ForEach(DeclaredProcedures, new ParallelOptions
+			{
+				MaxDegreeOfParallelism = Threads
+			}, kv =>
+#endif
+			{
+				Parser parser = new Parser(kv.Value, kv.Key, declaredFunctions, procedures, BaseDefinitions.DefaultGlobalVariables, kv.Key.Variables, BaseDefinitions.DefaultKeywords, csvDefinition, preprocessedConstants.ToArray());
+
+				var nodes = parser.Parse(out var localErrors, out var localWarnings);
+
+				if (localErrors.Count > 0)
+					throw new ParserException($"Failed to compile '{kv.Key.Name}'");
+
+				CompiledProcedures.Add(kv.Key, nodes.ToArray());
+#if DEBUG
+			}
+#else
+			});
+#endif
+		}
+
+		public IEnumerable<ExecutionNode> GetExecutionNodes(FunctionDefinition function)
+		{
+			return CompiledProcedures[function];
+		}
+	}
+}

+ 3 - 0
NTERA/formMain.cs

@@ -4,6 +4,7 @@ using NTERA.Console;
 using NTERA.Core;
 using NTERA.EmuEra;
 using NTERA.Engine;
+using NTERA.Engine.Runtime;
 
 namespace NTERA
 {
@@ -17,8 +18,10 @@ namespace NTERA
 
             //var scriptEngine = new EmuEraGameInstance();
             //var scriptEngine = new Engine();
+			var scriptEngine = new EraRuntime(new JITCompiler(@"M:\era\eraSemifullTest"));
 
             //Task.Factory.StartNew(() => instance.Run(new EraConsoleInstance(consoleControl1.Renderer, scriptEngine), scriptEngine));
+            Task.Factory.StartNew(() => instance.Run(new EraConsoleInstance(consoleControl1.Renderer, scriptEngine), scriptEngine));
 		}
 
 		private void txtInput_KeyDown(object sender, KeyEventArgs e)