Преглед изворни кода

Implement DO loops and image loading

Bepsi пре 6 година
родитељ
комит
d86abdc0d3

+ 46 - 4
NTERA.Engine/Compiler/Parser.cs

@@ -1114,17 +1114,21 @@ namespace NTERA.Engine.Compiler
 
 		protected void Branchify(List<ExecutionNode> nodes)
 		{
-			Stack<ExecutionNode> workingStack = new Stack<ExecutionNode>();
+			Stack<ExecutionNode> forNodeStack = new Stack<ExecutionNode>();
+			Stack<ExecutionNode> doNodeStack = new Stack<ExecutionNode>();
 
 			foreach (var node in nodes)
 			{
-				if (node.Type == "statement" && node["name"].Equals("FOR", StringComparison.OrdinalIgnoreCase))
+				if (node.Type == "statement")
 				{
-					workingStack.Push(node);
+					if (node["name"].Equals("FOR", StringComparison.OrdinalIgnoreCase))
+						forNodeStack.Push(node);
+					else if (node["name"].Equals("DO", StringComparison.OrdinalIgnoreCase))
+						doNodeStack.Push(node);
 				}
 			}
 
-			foreach (var forNode in workingStack)
+			foreach (var forNode in forNodeStack)
 			{
 				int index = nodes.IndexOf(forNode);
 
@@ -1160,6 +1164,44 @@ namespace NTERA.Engine.Compiler
 
 				nodes.Insert(index, newNode);
 			}
+
+			foreach (var doNode in doNodeStack)
+			{
+				int index = nodes.IndexOf(doNode);
+
+				int endIndex = 0;
+
+				for (int i = index; i < nodes.Count; i++)
+				{
+					var node = nodes[i];
+					if (node.Type == "statement" && node["name"].Equals("LOOP", StringComparison.OrdinalIgnoreCase))
+					{
+						endIndex = i;
+						break;
+					}
+				}
+
+				if (endIndex == 0)
+					throw new ParserException("Could not find matching LOOP for DO statement");
+
+				List<ExecutionNode> subNodes = new List<ExecutionNode>();
+
+				var loopNode = nodes[endIndex];
+				loopNode.Type = "loop-context";
+				subNodes.Add(loopNode);
+
+				subNodes.AddRange(nodes.Skip(index + 1).Take(endIndex - index - 1));
+
+				nodes.RemoveRange(index, (endIndex - index) + 1);
+
+				ExecutionNode newNode = new ExecutionNode
+				{
+					Type = "do",
+					SubNodes = subNodes.ToArray()
+				};
+
+				nodes.Insert(index, newNode);
+			}
 		}
 
 		#endregion

+ 1 - 1
NTERA.Engine/Compiler/Preprocessor.cs

@@ -318,7 +318,7 @@ namespace NTERA.Engine.Compiler
 			return procedures;
 		}
 
-		private static IList<IList<string>> SplitCSV(IEnumerable<string> lines)
+		public static IList<IList<string>> SplitCSV(IEnumerable<string> lines)
 		{
 			List<IList<string>> csv = new List<IList<string>>();
 

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

@@ -47,6 +47,7 @@
     <Compile Include="Compiler\Preprocessor.cs" />
     <Compile Include="Attributes.cs" />
     <Compile Include="Compiler\Lexer.cs" />
+    <Compile Include="Runtime\Resources\ImageDefinition.cs" />
     <Compile Include="Marker.cs" />
     <Compile Include="Compiler\ParserException.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />

+ 46 - 29
NTERA.Engine/Runtime/EraRuntime.cs

@@ -40,6 +40,7 @@ namespace NTERA.Engine.Runtime
 			TotalProcedureDefinitions.Clear();
 			GlobalVariables.Clear();
 
+			ExecutionProvider.Initialize(console);
 
 			TotalProcedureDefinitions.AddRange(ExecutionProvider.DefinedProcedures);
 			TotalProcedureDefinitions.AddRange(BaseDefinitions.DefaultGlobalFunctions);
@@ -82,7 +83,10 @@ namespace NTERA.Engine.Runtime
 				Console.PrintSystemLine("Stack trace:");
 
 				foreach (var stackMember in ExecutionStack)
-					Console.PrintSystemLine($"  - @{stackMember.SelfDefinition.Name} ({stackMember.SelfDefinition.Position} > {stackMember.SelfDefinition.Filename})");
+				{
+					string name = stackMember.IsAnonymous ? "<anonymous>" : $"@{stackMember.SelfDefinition.Name}";
+					Console.PrintSystemLine($"  - {name} ({stackMember.SelfDefinition.Position} > {stackMember.SelfDefinition.Filename})");
+				}
 
 				throw;
 			}
@@ -167,12 +171,15 @@ namespace NTERA.Engine.Runtime
 
 			if (context.ExecutionIndex >= context.ExecutionNodes.Count)
 			{
-				ExecutionStack.Pop();
+				if (!context.IsAnonymous || context.AnonymousExitCondition(context))
+				{
+					ExecutionStack.Pop();
 
-				if (!context.IsAnonymous && context.SelfDefinition.IsReturnFunction)
-					throw new EraRuntimeException("Function did not return a value");
+					if (!context.IsAnonymous && context.SelfDefinition.IsReturnFunction)
+						throw new EraRuntimeException("Function did not return a value");
 
-				return;
+					return;
+				}
 			}
 
 			ExecutionNode node = context.ExecutionNodes[context.ExecutionIndex++];
@@ -187,33 +194,43 @@ namespace NTERA.Engine.Runtime
 				var endNumber = ComputeExpression(context, forContext[2]);
 
 				iterationVariable[iterationIndex] = beginNumber;
+				
+				var newContext = context.Clone(node.Skip(1).ToList());
 
-				var subset = node.Skip(1);
-
-				for (double i = beginNumber.Real; i < endNumber.Real; i++)
+				newContext.AnonymousExitCondition = frame =>
 				{
-					var currentSubset = subset.ToList();
+					iterationVariable[iterationIndex]++;
 
-					var variableNode = Parser.GetVariable(iterationVariable.Name, Marker.Zero, iterationIndex.Select(x => Parser.CreateConstant(x, Marker.Zero)).ToArray());
+					if (iterationVariable[iterationIndex] >= endNumber)
+						return true;
 
-					//increment the iterationVariable
-					currentSubset.Add(new ExecutionNode
-					{
-						Type = "assignment",
-						Symbol = Marker.Zero,
-						SubNodes = new[]
-						{
-							variableNode,
-							new ExecutionNode
-							{
-								Type = "value",
-								SubNodes = new[] { Parser.OperateNodes(variableNode, Parser.CreateConstant(1, Marker.Zero), Token.Plus) }
-							}
-						}
-					});
+					frame.ExecutionIndex = 0;
+					return false;
+				};
 
-					ExecutionStack.Push(context.Clone(currentSubset));
-				}
+				ExecutionStack.Push(newContext);
+
+				return;
+			}
+			
+			if (node.Type == "do")
+			{
+				ExecutionNode loopContext = node[0];
+				
+				var loopVariable = ComputeVariable(context, loopContext[0], out var loopIndex);
+				
+				var newContext = context.Clone(node.Skip(1).ToList());
+
+				newContext.AnonymousExitCondition = frame =>
+				{
+					if (!loopVariable[loopIndex])
+						return true;
+
+					frame.ExecutionIndex = 0;
+					return false;
+				};
+
+				ExecutionStack.Push(newContext);
 
 				return;
 			}
@@ -396,8 +413,8 @@ namespace NTERA.Engine.Runtime
 
 		public CroppedImage GetImage(string name)
 		{
-			var bitmap = new Bitmap(@"M:\era\eraSemifullTest\resources\bbb.png");
-			return new CroppedImage(name, bitmap, new Rectangle(Point.Empty, bitmap.Size), false);
+			var bitmap = ExecutionProvider.GetImage(name, out var definition);
+			return new CroppedImage(name, bitmap, definition.Dimensions ?? new Rectangle(Point.Empty, bitmap.Size), false);
 		}
 	}
 }

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

@@ -1,15 +1,22 @@
 using System.Collections.Generic;
+using System.Drawing;
+using NTERA.Core;
 using NTERA.Engine.Compiler;
+using NTERA.Engine.Runtime.Resources;
 
 namespace NTERA.Engine.Runtime
 {
 	public interface IExecutionProvider
 	{
+		void Initialize(IConsole console);
+
 		ICollection<FunctionDefinition> DefinedProcedures { get; }
 		ICollection<FunctionDefinition> DefinedFunctions { get; }
 		ICollection<FunctionVariable> DefinedConstants { get; }
 		CSVDefinition CSVDefinition { get; }
 
 		IEnumerable<ExecutionNode> GetExecutionNodes(FunctionDefinition function);
+
+		Bitmap GetImage(string imageName, out ImageDefinition definition);
 	}
 }

+ 75 - 17
NTERA.Engine/Runtime/JITCompiler.cs

@@ -1,12 +1,16 @@
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
 using System.IO;
 using System.Linq;
 using System.Runtime;
 using System.Text;
 using System.Threading.Tasks;
+using NTERA.Core;
 using NTERA.Engine.Compiler;
+using NTERA.Engine.Runtime.Resources;
 
 namespace NTERA.Engine.Runtime
 {
@@ -17,48 +21,80 @@ namespace NTERA.Engine.Runtime
 		public ICollection<FunctionVariable> DefinedConstants { get; protected set; }
 		public CSVDefinition CSVDefinition { get; protected set; }
 
+		protected Dictionary<string, ImageDefinition> ImageDefinitions { get; set; }
+
 		protected Dictionary<FunctionDefinition, string> ProcedureFiles { get; set; }
 		public Dictionary<FunctionDefinition, ExecutionNode[]> CompiledProcedures { get; protected set; } = new Dictionary<FunctionDefinition, ExecutionNode[]>();
 
 		public string InputDirectory { get; }
+		protected string CSVPath { get; set; }
+		protected string ERBPath { get; set; }
+		protected string ResourcePath { get; set; }
 
 		public static int Threads = 4;
 
 		public JITCompiler(string path)
 		{
 			InputDirectory = path;
-
-			Compile();
 		}
 
-		protected void Compile()
+		public void Initialize(IConsole console)
 		{
-			string csvPath = Path.Combine(InputDirectory, "CSV");
-			string erbPath = Path.Combine(InputDirectory, "ERB");
+			Stopwatch stopwatch = new Stopwatch();
+			stopwatch.Start();
+
+			CSVPath = Path.Combine(InputDirectory, "CSV");
+			ERBPath = Path.Combine(InputDirectory, "ERB");
+			ResourcePath = Path.Combine(InputDirectory, "resources");
 
-			var csvDefinition = new CSVDefinition();
+			CSVDefinition = new CSVDefinition();
+			ImageDefinitions = new Dictionary<string, ImageDefinition>();
 
-			Console.WriteLine("Preprocessing CSV files...");
+			console.PrintSystemLine("Preprocessing CSV files...");
 
 
-			foreach (var file in Directory.EnumerateFiles(csvPath, "*.csv", SearchOption.AllDirectories))
+			foreach (var file in Directory.EnumerateFiles(CSVPath, "*.csv", SearchOption.AllDirectories))
 			{
-				string path = Path.GetFileNameWithoutExtension(file);
+				string localName = Path.GetFileNameWithoutExtension(file);
 
-				if (path.EndsWith("_TR"))
+				if (localName == null || localName.EndsWith("_TR"))
 					continue;
 
-				Preprocessor.ProcessCSV(csvDefinition, path, File.ReadLines(file, Encoding.UTF8));
+				Preprocessor.ProcessCSV(CSVDefinition, localName, File.ReadLines(file, Encoding.UTF8));
+			}
+
+
+			foreach (var file in Directory.EnumerateFiles(ResourcePath, "*.csv", SearchOption.TopDirectoryOnly))
+			{
+				foreach (var line in Preprocessor.SplitCSV(File.ReadLines(file)))
+				{
+					if (line.Count != 2 && line.Count != 6)
+						throw new ParserException($"Unable to parse resource CSV file '{Path.GetFileName(file)}'");
+
+					ImageDefinition definition = new ImageDefinition
+					{
+						Name = line[0],
+						Filename = line[1],
+						Dimensions = null
+					};
+
+					if (line.Count == 6)
+					{
+						definition.Dimensions = new Rectangle(int.Parse(line[2]), int.Parse(line[3]), int.Parse(line[4]), int.Parse(line[5]));
+					}
+
+					ImageDefinitions[definition.Name] = definition;
+				}
 			}
 
-			Console.WriteLine("Preprocessing header files...");
+			console.PrintSystemLine("Preprocessing header files...");
 
 			ConcurrentBag<FunctionVariable> preprocessedConstants = new ConcurrentBag<FunctionVariable>();
 
 #if DEBUG
-			foreach (var file in Directory.EnumerateFiles(erbPath, "*.erh", SearchOption.AllDirectories))
+			foreach (var file in Directory.EnumerateFiles(ERBPath, "*.erh", SearchOption.AllDirectories))
 #else
-			Parallel.ForEach(Directory.EnumerateFiles(erbPath, "*.erh", SearchOption.AllDirectories), new ParallelOptions
+			Parallel.ForEach(Directory.EnumerateFiles(ERBPath, "*.erh", SearchOption.AllDirectories), new ParallelOptions
 			{
 				MaxDegreeOfParallelism = Threads
 			}, file =>
@@ -74,15 +110,15 @@ namespace NTERA.Engine.Runtime
 
 			DefinedConstants = preprocessedConstants.ToArray();
 
-			Console.WriteLine("Preprocessing functions...");
+			console.PrintSystemLine("Preprocessing functions...");
 
 			ProcedureFiles = new Dictionary<FunctionDefinition, string>();
 			ConcurrentDictionary<FunctionDefinition, string> preprocessedFunctions = new ConcurrentDictionary<FunctionDefinition, string>();
 
 #if DEBUG
-			foreach (var file in Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories))
+			foreach (var file in Directory.EnumerateFiles(ERBPath, "*.erb", SearchOption.AllDirectories))
 #else
-			Parallel.ForEach(Directory.EnumerateFiles(erbPath, "*.erb", SearchOption.AllDirectories), new ParallelOptions
+			Parallel.ForEach(Directory.EnumerateFiles(ERBPath, "*.erb", SearchOption.AllDirectories), new ParallelOptions
 			{
 				MaxDegreeOfParallelism = Threads
 			}, file =>
@@ -104,8 +140,13 @@ namespace NTERA.Engine.Runtime
 			declaredFunctions.AddRange(DefinedProcedures.Where(x => x.IsReturnFunction));
 			DefinedFunctions = declaredFunctions;
 
+			console.PrintSystemLine("Compacting memory...");
+
 			GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
 			GC.Collect();
+
+			stopwatch.Stop();
+			console.PrintSystemLine($"Completed initialization in {stopwatch.ElapsedMilliseconds}ms");
 		}
 
 		public IEnumerable<ExecutionNode> GetExecutionNodes(FunctionDefinition function)
@@ -127,5 +168,22 @@ namespace NTERA.Engine.Runtime
 
 			return nodes;
 		}
+
+		protected Dictionary<string, Bitmap> BitmapCache = new Dictionary<string, Bitmap>();
+
+		public Bitmap GetImage(string imageName, out ImageDefinition definition)
+		{
+			definition = ImageDefinitions[imageName];
+
+			string filename = Path.Combine(ResourcePath, definition.Filename);
+
+			if (!BitmapCache.TryGetValue(filename, out var bitmap))
+			{
+				bitmap = new Bitmap(filename);
+				BitmapCache.Add(filename, bitmap);
+			}
+
+			return bitmap;
+		}
 	}
 }

+ 13 - 0
NTERA.Engine/Runtime/Resources/ImageDefinition.cs

@@ -0,0 +1,13 @@
+using System.Drawing;
+
+namespace NTERA.Engine.Runtime.Resources
+{
+	public class ImageDefinition
+	{
+		public string Name { get; set; }
+
+		public string Filename { get; set; }
+
+		public Rectangle? Dimensions { get; set; }
+	}
+}

+ 4 - 1
NTERA.Engine/Runtime/StackFrame.cs

@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using NTERA.Engine.Compiler;
 
 namespace NTERA.Engine.Runtime
@@ -17,6 +18,8 @@ namespace NTERA.Engine.Runtime
 
 		public IList<ExecutionNode> ExecutionNodes { get; set; }
 
+		public Func<StackFrame, bool> AnonymousExitCondition { get; set; } = null;
+
 		public StackFrame Clone(IList<ExecutionNode> nodes, bool anonymous = true)
 		{
 			return new StackFrame