Browse Source

Reimplement variable handling

Bepsi 6 years ago
parent
commit
de0308dbbb

+ 21 - 0
NTERA.Compiler/Compiler.cs

@@ -5,6 +5,7 @@ using System.IO;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using System.Xml;
 using NTERA.Engine;
 using NTERA.Engine.Compiler;
 using NTERA.Engine.Runtime;
@@ -131,6 +132,26 @@ namespace NTERA.Compiler
 
 					var nodes = parser.Parse(out var localErrors, out var localWarnings);
 
+					using (var str = File.Create(Path.Combine(outputDirectory, $"{kv.Key.Name}.xml")))
+					using (var xml = XmlWriter.Create(str, new XmlWriterSettings
+					{
+						Indent = true
+					}))
+					{
+						new ExecutionNode
+						{
+							Type = "exec",
+							Metadata =
+							{
+								["ast-version"] = "2",
+									["name"] = kv.Key.Name,
+									["filename"] = kv.Key.Filename,
+									["position"] = kv.Key.Position.ToString()
+							},
+							SubNodes = nodes.ToArray()
+						}.WriteXml(xml);
+					}
+
 					lock (Errors)
 						Errors.AddRange(localErrors.Select(x => new Tuple<ParserError, string>(x, $"{kv.Key.Filename} - {kv.Key.Name}")));
 

+ 3 - 0
NTERA.Compiler/NTERA.Compiler.csproj

@@ -25,6 +25,7 @@
     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <PlatformTarget>AnyCPU</PlatformTarget>
@@ -34,6 +35,7 @@
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="Costura, Version=3.1.6.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
@@ -42,6 +44,7 @@
     </Reference>
     <Reference Include="System" />
     <Reference Include="System.Core" />
+    <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Compiler.cs" />

+ 8 - 2
NTERA.Engine/Compiler/ExecutionNode.cs

@@ -19,9 +19,10 @@ namespace NTERA.Engine.Compiler
 
 		public ExecutionNode[] SubNodes { get; set; } = new ExecutionNode[0];
 
-		public ExecutionNode this[string type]
+		public string this[string type]
 		{
-			get { return SubNodes.First(x => x.Type == type); }
+			get => Metadata[type];
+			set => Metadata[type] = value;
 		}
 
 		public ExecutionNode this[int index]
@@ -30,6 +31,11 @@ namespace NTERA.Engine.Compiler
 			set => SubNodes[index] = value;
 		}
 
+		public ExecutionNode GetSubtype(string subType)
+		{
+			return SubNodes.Single(x => x.Type == subType);
+		}
+
 		public void WriteXml(XmlWriter writer)
 		{
 			writer.WriteStartElement(Type);

+ 4 - 4
NTERA.Engine/Compiler/FunctionDefinition.cs

@@ -51,7 +51,7 @@ namespace NTERA.Engine.Compiler
 
 		public Value? DefaultValue { get; }
 
-		public Value CalculatedValue => DefaultValue.HasValue ? DefaultValue.Value : ValueType == ValueType.String ? new Value("") : new Value(0d); //for some reason, this can't be null coalesce (??), as it disregards the value
+		public Value CalculatedValue => DefaultValue ?? (ValueType == ValueType.String ? new Value("") : new Value(0d));
 
 		public FunctionVariable(string name, ValueType valueType, VariableType variableType = VariableType.None, Value? defaultValue = null)
 		{
@@ -66,16 +66,16 @@ namespace NTERA.Engine.Compiler
 	{
 		public string Name { get; }
 
-		public string[] Indices { get; }
+		public int[] Index { get; }
 
 		public bool IsArrayParameter { get; }
 
 		public Value? DefaultValue { get; }
 
-		public FunctionParameter(string name, string[] indices, Value? defaultValue = null, bool isArrayParameter = false)
+		public FunctionParameter(string name, int[] index = null, Value? defaultValue = null, bool isArrayParameter = false)
 		{
 			Name = name;
-			Indices = indices;
+			Index = index ?? new[] { 0 };
 			DefaultValue = defaultValue;
 			IsArrayParameter = isArrayParameter;
 		}

+ 3 - 5
NTERA.Engine/Compiler/Preprocessor.cs

@@ -182,7 +182,7 @@ namespace NTERA.Engine.Compiler
 						while (enumerator.Current == Token.Identifer)
 						{
 							string parameterName = lexer.Identifier;
-							List<string> indices = new List<string>();
+							List<int> indices = new List<int>();
 							Value? defaultValue = null;
 
 							enumerator.MoveNext();
@@ -192,13 +192,11 @@ namespace NTERA.Engine.Compiler
 							{
 								if (enumerator.Current == Token.Value)
 								{
-									indices.Add(lexer.Value.Type == ValueType.Real
-										? ((int)lexer.Value).ToString()
-										: lexer.Value.String);
+									indices.Add((int)lexer.Value.Real);
 								}
 								else if (enumerator.Current == Token.Identifer)
 								{
-									indices.Add(lexer.Identifier);
+									indices.Add((int)ConstantExpression(lexer, constantDefinitions).Real);
 								}
 
 								enumerator.MoveNext();

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

@@ -62,7 +62,6 @@
     <Compile Include="Utility.cs" />
     <Compile Include="Value.cs" />
     <Compile Include="Variable.cs" />
-    <Compile Include="VariableDictionary.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\NTERA.Core\NTERA.Core.csproj">

+ 2 - 7
NTERA.Engine/Runtime/Base/Keywords.cs

@@ -50,14 +50,9 @@ namespace NTERA.Engine.Runtime.Base
 		[Keyword("TIMES")]
 		public static void Times(EraRuntime runtime, StackFrame context, ExecutionNode node)
 		{
-			string variableName = node[0].Metadata["name"];
+			Variable variable = runtime.ComputeVariable(context, node[0], out var index);
 
-			if (context.GlobalVariables.ContainsKey(variableName))
-				context.GlobalVariables[variableName] *= runtime.ComputeExpression(context, node[1]);
-			else if (context.GlobalVariables.ContainsKey(variableName))
-				context.GlobalVariables[variableName] *= runtime.ComputeExpression(context, node[1]);
-			else
-				throw new EraRuntimeException("Invalid parameters specified for TIMES");
+			variable[index] *= runtime.ComputeExpression(context, node[1]);
 		}
 
 		[Keyword("ALIGNMENT")]

+ 48 - 48
NTERA.Engine/Runtime/BaseDefinitions.cs

@@ -155,53 +155,53 @@ namespace NTERA.Engine.Runtime
 
 		public static FunctionDefinition[] DefaultGlobalFunctions { get; } =
 		{
-			new FunctionDefinition("ABS", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("SQRT", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("SIGN", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("CSVCALLNAME", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("TOFULL", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("TOINT", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("RAND", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("VARSIZE", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("TOSTR", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0], "") }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("TOUPPER", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("TOLOWER", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("TOHALF", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("TOFULL", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("TOUPPER", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("ISNUMERIC", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("LOG10", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("ESCAPE", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("STRCOUNT", new[] { new FunctionParameter("input", new string[0]), new FunctionParameter("match", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("MIN", new[] { new FunctionParameter("a", new string[0], isArrayParameter: true) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("MAX", new[] { new FunctionParameter("a", new string[0], isArrayParameter: true) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("POWER", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("GETPALAMLV", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("GETBIT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("GETEXPLV", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("UNICODE", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("MATCH", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]), new FunctionParameter("c", new string[0], "a"), new FunctionParameter("d", new string[0], "f") }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("INRANGE", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]), new FunctionParameter("c", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("HE_SHE", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("SUBSTRING", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("STRLENS", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("CSVNAME", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("CSVNAME", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("CSVBASE", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("CSVTALENT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("CSVCFLAG", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("CSVRELATION", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("CSVCSTR", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("CSVEXP", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("CSVABL", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("GETNUM", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("FINDCHARA", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("LIMIT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("SUMCARRAY", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("MAXCARRAY", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("SUBSTRINGU", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("FINDELEMENT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "__GLOBAL"),
-			new FunctionDefinition("GROUPMATCH", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0], isArrayParameter: true) }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("ABS", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("SQRT", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("SIGN", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("CSVCALLNAME", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("TOFULL", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("TOINT", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("RAND", new[] { new FunctionParameter("a"), new FunctionParameter("b") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("VARSIZE", new[] { new FunctionParameter("a"), new FunctionParameter("b") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("TOSTR", new[] { new FunctionParameter("a"), new FunctionParameter("b") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("TOUPPER", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("TOLOWER", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("TOHALF", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("TOFULL", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("TOUPPER", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("ISNUMERIC", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("LOG10", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("ESCAPE", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("STRCOUNT", new[] { new FunctionParameter("input"), new FunctionParameter("match") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("MIN", new[] { new FunctionParameter("a", isArrayParameter: true) }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("MAX", new[] { new FunctionParameter("a", isArrayParameter: true) }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("POWER", new[] { new FunctionParameter("a"), new FunctionParameter("b") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("GETPALAMLV", new[] { new FunctionParameter("a"), new FunctionParameter("b") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("GETBIT", new[] { new FunctionParameter("a"), new FunctionParameter("b") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("GETEXPLV", new[] { new FunctionParameter("a"), new FunctionParameter("b") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("UNICODE", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("MATCH", new[] { new FunctionParameter("a"), new FunctionParameter("b"), new FunctionParameter("c"), new FunctionParameter("d") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("INRANGE", new[] { new FunctionParameter("a"), new FunctionParameter("b"), new FunctionParameter("c") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("HE_SHE", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("SUBSTRING", new[] { new FunctionParameter("a"), new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("STRLENS", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("CSVNAME", new[] { new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("CSVNAME", new[] { new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("CSVBASE", new[] { new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("CSVTALENT", new[] { new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("CSVCFLAG", new[] { new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("CSVRELATION", new[] { new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("CSVCSTR", new[] { new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("CSVEXP", new[] { new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("CSVABL", new[] { new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("GETNUM", new[] { new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("FINDCHARA", new[] { new FunctionParameter("a"), new FunctionParameter("a"), new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("LIMIT", new[] { new FunctionParameter("a"), new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("SUMCARRAY", new[] { new FunctionParameter("a"), new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("MAXCARRAY", new[] { new FunctionParameter("a"), new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("SUBSTRINGU", new[] { new FunctionParameter("a"), new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("FINDELEMENT", new[] { new FunctionParameter("a"), new FunctionParameter("a"), new FunctionParameter("a"), new FunctionParameter("a"), new FunctionParameter("a") }, new FunctionVariable[0], true, "__GLOBAL"),
+			new FunctionDefinition("GROUPMATCH", new[] { new FunctionParameter("a"), new FunctionParameter("a", isArrayParameter: true) }, new FunctionVariable[0], true, "__GLOBAL"),
 			new FunctionDefinition("GETTIME", new FunctionParameter[0], new FunctionVariable[0], true, "__GLOBAL"),
 			new FunctionDefinition("SAVENOS", new FunctionParameter[0], new FunctionVariable[0], true, "__GLOBAL"),
 			new FunctionDefinition("GETCOLOR", new FunctionParameter[0], new FunctionVariable[0], true, "__GLOBAL"),
@@ -209,7 +209,7 @@ namespace NTERA.Engine.Runtime
 			new FunctionDefinition("GETFONT", new FunctionParameter[0], new FunctionVariable[0], true, "__GLOBAL"),
 			new FunctionDefinition("GETMILLISECOND", new FunctionParameter[0], new FunctionVariable[0], true, "__GLOBAL"),
 
-			new FunctionDefinition("__FORMAT",  new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "__GLOBAL")
+			new FunctionDefinition("__FORMAT",  new[] { new FunctionParameter("a"), new FunctionParameter("a"), new FunctionParameter("a"), }, new FunctionVariable[0], true, "__GLOBAL")
 		};
 
 		public static Keyword[] DefaultKeywords { get; } =

+ 108 - 38
NTERA.Engine/Runtime/EraRuntime.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Drawing;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using NTERA.Core;
 using NTERA.EmuEra.Game.EraEmu.Content;
 using NTERA.Engine.Compiler;
@@ -56,15 +57,24 @@ namespace NTERA.Engine.Runtime
 
 		public ExecutionResult Call(FunctionDefinition function, IList<Value> parameters = null)
 		{
-			var localVariables = new VariableDictionary();
+			var localVariables = new Dictionary<string, Variable>();
+			var globalVariables = new Dictionary<string, Variable>();
 
 			foreach (var variable in function.Variables)
-				localVariables[variable.Name] = variable.CalculatedValue;
+			{
+				var localVariable = new Variable(variable.Name, variable.ValueType);
+				localVariable[0] = variable.CalculatedValue;
 
-			var globalVariables = new VariableDictionary();
+				localVariables.Add(variable.Name, localVariable);
+			}
 
 			foreach (var variable in BaseDefinitions.DefaultGlobalVariables)
-				globalVariables[variable.Name] = variable.CalculatedValue;
+			{
+				var globalVariable = new Variable(variable.Name, variable.ValueType);
+				globalVariable[0] = variable.CalculatedValue;
+
+				globalVariables.Add(variable.Name, globalVariable);
+			}
 
 			var context = new StackFrame
 			{
@@ -86,6 +96,7 @@ namespace NTERA.Engine.Runtime
 			else
 			{
 				if (parameters != null)
+				{
 					for (var index = 0; index < parameters.Count; index++)
 					{
 						FunctionParameter parameter;
@@ -97,13 +108,11 @@ namespace NTERA.Engine.Runtime
 						else
 							throw new EraRuntimeException($"Unable to assign parameter #{index + 1}");
 
-						if (localVariables.ContainsKey(parameter.Name))
-							localVariables[parameter.Name] = parameters[index];
-						else if (globalVariables.ContainsKey(parameter.Name))
-							globalVariables[parameter.Name] = parameters[index];
-						else
-							throw new EraRuntimeException($"Unable to assign parameter '{parameter.Name}' to a variable");
+						var paramVariable = ComputeVariable(context, parameter.Name);
+							
+						paramVariable[parameter.Index] = parameters[index];
 					}
+				}
 
 				var executionContents = ExecutionProvider.GetExecutionNodes(function);
 
@@ -121,11 +130,53 @@ namespace NTERA.Engine.Runtime
 			{
 				ExecutionNode node = nodes[index];
 
+				if (node.Type == "statement")
+				{
+					if (node["name"].Equals("FOR", StringComparison.OrdinalIgnoreCase))
+					{
+						int endIndex = -1;
+						int forLevel = 0;
+
+						for (int subIndex = index + 1; subIndex < nodes.Count; subIndex++)
+						{
+							ExecutionNode subNode = nodes[subIndex];
+
+							if (subNode.Type != "statement")
+								continue;
+
+							if (subNode["name"].Equals("FOR", StringComparison.OrdinalIgnoreCase))
+								forLevel++;
+
+							if (subNode["name"].Equals("NEXT", StringComparison.OrdinalIgnoreCase))
+							{
+								if (forLevel == 0)
+								{
+									endIndex = subIndex;
+									break;
+								}
+
+								forLevel--;
+							}
+						}
+
+						if (endIndex == -1)
+							throw new EraRuntimeException("Could not find matching NEXT for FOR statement");
+
+						//string variableName = 
+
+						//for (context.LocalVariables[])
+						ExecuteSet(context, nodes.Skip(index).Take(endIndex - index).ToArray());
+
+						index = endIndex;
+						continue;
+					}
+				}
+
 				if (node.Type == "result")
 				{
 					return new ExecutionResult(ExecutionResultType.FunctionReturn, ComputeExpression(context, node.Single()));
 				}
-				
+
 				ExecuteNode(context, node);
 			}
 
@@ -137,7 +188,7 @@ namespace NTERA.Engine.Runtime
 			switch (node.Type)
 			{
 				case "statement":
-					string statement = node.Metadata["name"];
+					string statement = node["name"];
 
 					if (!Base.Keywords.StaticKeywords.TryGetValue(statement, out var keywordAction))
 						throw new EraRuntimeException($"Unknown statement: '{statement}'");
@@ -147,25 +198,20 @@ namespace NTERA.Engine.Runtime
 					return;
 
 				case "assignment":
-					string variableName = node["variable"].Metadata["name"];
+					Variable variable = ComputeVariable(context, node.GetSubtype("variable"), out var index);
 
-					if (context.LocalVariables.ContainsKey(variableName))
-						context.LocalVariables[variableName] = ComputeExpression(context, node["value"].Single());
-					else if (context.GlobalVariables.ContainsKey(variableName))
-						context.GlobalVariables[variableName] = ComputeExpression(context, node["value"].Single());
-					else
-						throw new EraRuntimeException($"Unknown variable: '{variableName}'");
+					variable[index] = ComputeExpression(context, node.GetSubtype("value").Single());
 
 					return;
 
 				case "call":
-					string procedureName = node.Metadata["target"];
+					string procedureName = node["target"];
 					var procedure = TotalProcedureDefinitions.FirstOrDefault(func => !func.IsReturnFunction && func.Name.Equals(procedureName, StringComparison.OrdinalIgnoreCase));
 
 					if (procedure == null)
 						throw new EraRuntimeException($"Unknown procedure: '{procedureName}'");
 
-					var executionResult = Call(procedure, node["parameters"].Select(x => ComputeExpression(context, x)).ToArray());
+					var executionResult = Call(procedure, node.GetSubtype("parameters").Select(x => ComputeExpression(context, x)).ToArray());
 
 					if (executionResult.Type != ExecutionResultType.None || executionResult.Result.HasValue)
 						throw new EraRuntimeException($"Unexpected result from procedure '{procedureName}': {executionResult.Type}");
@@ -177,35 +223,56 @@ namespace NTERA.Engine.Runtime
 			}
 		}
 
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		public Variable ComputeVariable(StackFrame context, ExecutionNode variableNode, out int[] index)
+		{
+			string variableName = variableNode["name"];
+			index = new[] { 0 };
+
+			if (variableNode.SubNodes.Any(x => x.Type == "index"))
+			{
+				ExecutionNode indexNode = variableNode.GetSubtype("index");
+
+				index = indexNode.SubNodes.Select(x => (int)ComputeExpression(context, x)).ToArray();
+			}
+
+			return ComputeVariable(context, variableName);
+		}
+
+		[MethodImpl(MethodImplOptions.AggressiveInlining)]
+		public Variable ComputeVariable(StackFrame context, string variableName)
+		{
+			if (context.LocalVariables.TryGetValue(variableName, out var variable)
+				|| context.GlobalVariables.TryGetValue(variableName, out variable))
+				return variable;
+
+			throw new EraRuntimeException($"Unable to retrieve variable '{variableName}'");
+		}
+
 		public Value ComputeExpression(StackFrame context, ExecutionNode expressionNode)
 		{
 			switch (expressionNode.Type)
 			{
 				case "constant":
-					ValueType type = (ValueType)Enum.Parse(typeof(ValueType), expressionNode.Metadata["type"]);
+					ValueType type = (ValueType)Enum.Parse(typeof(ValueType), expressionNode["type"]);
 
-					string strValue = expressionNode.Metadata["value"];
+					string strValue = expressionNode["value"];
 
 					return type == ValueType.String ? (Value)strValue : (Value)double.Parse(strValue);
 
 				case "variable":
-					string variableName = expressionNode.Metadata["name"];
+					Variable variable = ComputeVariable(context, expressionNode, out var index);
 
-					if (context.LocalVariables.ContainsKey(variableName))
-						return context.LocalVariables[variableName];
-					else if (context.GlobalVariables.ContainsKey(variableName))
-						return context.GlobalVariables[variableName];
-					else
-						throw new EraRuntimeException($"Unable to retrieve variable '{variableName}'");
+					return variable[index];
 
 				case "call":
-					string functionName = expressionNode.Metadata["target"];
+					string functionName = expressionNode["target"];
 					var function = TotalProcedureDefinitions.FirstOrDefault(func => func.IsReturnFunction && func.Name.Equals(functionName, StringComparison.OrdinalIgnoreCase));
 
 					if (function == null)
 						throw new EraRuntimeException($"Unknown function: '{functionName}'");
 
-					var executionResult = Call(function, expressionNode["parameters"].Select(x => ComputeExpression(context, x)).ToArray());
+					var executionResult = Call(function, expressionNode.GetSubtype("parameters").Select(x => ComputeExpression(context, x)).ToArray());
 
 					if (executionResult.Type != ExecutionResultType.FunctionReturn || !executionResult.Result.HasValue)
 						throw new EraRuntimeException($"Unexpected result from function '{functionName}': {executionResult.Type}");
@@ -213,15 +280,19 @@ namespace NTERA.Engine.Runtime
 					return executionResult.Result.Value;
 
 				case "operation":
-					bool isUnary = expressionNode.Metadata.ContainsKey("unary") && bool.Parse(expressionNode.Metadata["unary"]);
-					string operationType = expressionNode.Metadata["type"];
+					bool isUnary = expressionNode.Metadata.ContainsKey("unary") && bool.Parse(expressionNode["unary"]);
+					string operationType = expressionNode["type"];
 
 					Token operatorToken;
 
 					switch (operationType)
 					{
-						case "add": operatorToken = Token.Plus; break;
-						case "subtract": operatorToken = Token.Minus; break;
+						case "add":
+							operatorToken = Token.Plus;
+							break;
+						case "subtract":
+							operatorToken = Token.Minus;
+							break;
 						default: throw new EraRuntimeException($"Unknown operation type: '{operationType}'");
 					}
 
@@ -236,7 +307,7 @@ namespace NTERA.Engine.Runtime
 							default: throw new EraRuntimeException($"Unsupported unary operation type: '{operationType}'");
 						}
 					}
-					
+
 					var left = ComputeExpression(context, expressionNode[0]);
 					var right = ComputeExpression(context, expressionNode[1]);
 
@@ -266,7 +337,6 @@ namespace NTERA.Engine.Runtime
 		{
 			var bitmap = new Bitmap(@"M:\era\eraSemifullTest\resources\bbb.png");
 			return new CroppedImage(name, bitmap, new Rectangle(Point.Empty, bitmap.Size), false);
-
 		}
 	}
 }

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

@@ -1,11 +1,12 @@
-using NTERA.Engine.Compiler;
+using System.Collections.Generic;
+using NTERA.Engine.Compiler;
 
 namespace NTERA.Engine.Runtime
 {
 	public class StackFrame
 	{
-		public VariableDictionary LocalVariables { get; set; }
-		public VariableDictionary GlobalVariables { get; set; }
+		public Dictionary<string, Variable> LocalVariables { get; set; }
+		public Dictionary<string, Variable> GlobalVariables { get; set; }
 
 		public FunctionDefinition SelfDefinition { get; set; }
 	}

+ 82 - 10
NTERA.Engine/Variable.cs

@@ -1,24 +1,96 @@
-namespace NTERA.Engine
+using System.Collections.Generic;
+
+namespace NTERA.Engine
 {
 	public class Variable
 	{
 		public string Name { get; }
 
-		public int[] Index { get; }
+		public ValueType Type { get; }
 
-		protected VariableDictionary Dictionary { get; }
+		protected Dictionary<int[], Value> Dictionary { get; } = new Dictionary<int[], Value>(new ZeroIntegerArrayEqualityComparer());
+		
+		public Variable(string name, ValueType type)
+		{
+			Name = name;
+			Type = type;
+		}
 
-		public Value Value
+		public Value this[params int[] index]
 		{
-			get => Dictionary[Name, Index[0]];
-			set => Dictionary[Name, Index[0]] = value;
+			get => Dictionary[index];
+			set => Dictionary[index] = value;
 		}
+	}
 
-		public Variable(string name, int[] index, VariableDictionary dictionary)
+	public class ZeroIntegerArrayEqualityComparer : IEqualityComparer<int[]>
+	{
+		public bool Equals(int[] x, int[] y)
 		{
-			Name = name;
-			Index = index;
-			Dictionary = dictionary;
+			if (x == null && y != null
+				|| y == null && x != null)
+				return false;
+
+			if (x == null || x.Length == 0)
+				x = new[] { 0 };
+
+			if (y == null || y.Length == 0)
+				y = new[] { 0 };
+
+			int xNonZeroLength = x.Length - 1;
+			int yNonZeroLength = y.Length - 1;
+
+			for (; xNonZeroLength > 0; xNonZeroLength++)
+			{
+				if (x[xNonZeroLength] != 0)
+					break;
+			}
+
+			for (; yNonZeroLength > 0; yNonZeroLength++)
+			{
+				if (y[yNonZeroLength] != 0)
+					break;
+			}
+
+			if (xNonZeroLength != yNonZeroLength)
+			{
+				return false;
+			}
+
+			for (int i = 0; i < xNonZeroLength; i++)
+			{
+				if (x[i] != y[i])
+				{
+					return false;
+				}
+			}
+
+			return true;
+		}
+
+		public int GetHashCode(int[] obj)
+		{
+			if (obj.Length == 0)
+				return 17 * 23; //equal to int[] { 0 }
+
+			int nonZeroLength = obj.Length - 1;
+
+			for (; nonZeroLength > 0; nonZeroLength++)
+			{
+				if (obj[nonZeroLength] != 0)
+					break;
+			}
+
+			int result = 17;
+			for (int i = 0; i < nonZeroLength; i++)
+			{
+				unchecked
+				{
+					result = result * 23 + obj[i];
+				}
+			}
+
+			return result;
 		}
 	}
 }

+ 0 - 52
NTERA.Engine/VariableDictionary.cs

@@ -1,52 +0,0 @@
-using System;
-using System.Collections.Generic;
-
-namespace NTERA.Engine
-{
-	public class VariableDictionary : Dictionary<string, Dictionary<int, Value>>
-	{
-		public VariableDictionary() : base(StringComparer.InvariantCultureIgnoreCase)
-		{
-			InitDefaults();
-		}
-
-		public new Value this[string key]
-		{
-			get => this[key, 0];
-			set => this[key, 0] = value;
-		}
-
-		public Value this[string key, int arrayIndex]
-		{
-			get => base[key][arrayIndex];
-			set
-			{
-				if (base.TryGetValue(key, out var dict))
-					dict[arrayIndex] = value;
-				else
-					Add(key, arrayIndex, value);
-			}
-		}
-
-		public void Add(string key, Value value)
-		{
-			Add(key, 0, value);
-		}
-
-		public void Add(string key, int arrayIndex, Value value)
-		{
-			base.Add(key, new Dictionary<int, Value>
-			{
-				[arrayIndex] = value
-			});
-		}
-
-		private void InitDefaults()
-		{
-			//Add("LOCAL", 0);
-			//Add("LOCALS", "");
-			//Add("RESULT", 0);
-			//Add("RESULTS", "");
-		}
-	}
-}

+ 2 - 0
NTERA/NTERA.csproj

@@ -22,6 +22,7 @@
     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <PlatformTarget>AnyCPU</PlatformTarget>
@@ -31,6 +32,7 @@
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />