Ver código fonte

Implement basic function parameter and return value semantics

Bepsi 6 anos atrás
pai
commit
53a17d625a

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

@@ -910,6 +910,20 @@ namespace NTERA.Engine.Compiler
 			}
 
 			StringBuilder currentBlock = new StringBuilder();
+			void commitBlock()
+			{
+				if (currentBlock.Length == 0)
+					return;
+
+				ExecutionNode stringBlock = CreateConstant(currentBlock.ToString(), CurrentPosition);
+
+				value = value == null
+					? stringBlock
+					: OperateNodes(value, stringBlock, Token.Plus);
+
+				currentBlock.Clear();
+			}
+
 			while ((Lexer.CurrentChar != '"' || implicitString)
 				   && Lexer.CurrentChar != '\n'
 				   && Lexer.CurrentChar != '\0')
@@ -936,6 +950,8 @@ namespace NTERA.Engine.Compiler
 						if (error != null)
 							return null;
 
+						commitBlock();
+
 						value = value == null
 							? expressionValue
 							: OperateNodes(value, expressionValue, Token.Plus);
@@ -966,6 +982,8 @@ namespace NTERA.Engine.Compiler
 
 					var formattedValue = CallMethod("__FORMAT", symbolMarker, formatParams.ToArray());
 
+					commitBlock();
+
 					value = value == null
 						? formattedValue
 						: OperateNodes(value, formattedValue, Token.Plus);
@@ -984,11 +1002,9 @@ namespace NTERA.Engine.Compiler
 				return null;
 			}
 
-			ExecutionNode appendedValue = CreateConstant(currentBlock.ToString(), CurrentPosition);
+			commitBlock();
 
-			value = value == null
-				? appendedValue
-				: OperateNodes(value, appendedValue, Token.Plus);
+			value = value ?? CreateConstant("", CurrentPosition);
 
 			return value;
 		}

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

@@ -52,9 +52,10 @@
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Compiler\Token.cs" />
     <Compile Include="Runtime\BaseDefinitions.cs" />
+    <Compile Include="Runtime\Base\Functions.cs" />
     <Compile Include="Runtime\Base\Keywords.cs" />
     <Compile Include="Runtime\EraRuntime.cs" />
-    <Compile Include="Runtime\ExecutionSet.cs" />
+    <Compile Include="Runtime\StackFrame.cs" />
     <Compile Include="Runtime\IExecutionProvider.cs" />
     <Compile Include="Runtime\JITCompiler.cs" />
     <Compile Include="Utility.cs" />

+ 53 - 0
NTERA.Engine/Runtime/Base/Functions.cs

@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace NTERA.Engine.Runtime.Base
+{
+	public static class Functions
+	{
+		private class FunctionAttribute : Attribute
+		{
+			public string Name { get; }
+
+			public FunctionAttribute(string name)
+			{
+				Name = name;
+			}
+		}
+
+		public static Dictionary<string, Func<EraRuntime, StackFrame, IList<Value>, Value>> StaticFunctions { get; } = _getFunctions();
+
+		private static Dictionary<string, Func<EraRuntime, StackFrame, IList<Value>, Value>> _getFunctions()
+		{
+			var output = new Dictionary<string, Func<EraRuntime, StackFrame, IList<Value>, Value>>();
+
+			foreach (MethodInfo method in typeof(Functions).GetMethods(BindingFlags.Public | BindingFlags.Static))
+			{
+				var function = method.GetCustomAttribute<FunctionAttribute>();
+
+				if (function != null)
+					output[function.Name] = (runtime, set, parameters) => (Value)method.Invoke(null, new object[] { runtime, set, parameters });
+			}
+
+			return output;
+		}
+
+		[Function("__FORMAT")]
+		public static Value Format(EraRuntime runtime, StackFrame context, IList<Value> parameters)
+		{
+			if (parameters.Count == 1)
+				return parameters[0].String;
+
+			throw new NotImplementedException("Advanced formatting hasn't been implemented yet");
+		}
+
+		private static Random _random = new Random();
+
+		[Function("RAND")]
+		public static Value Rand(EraRuntime runtime, StackFrame context, IList<Value> parameters)
+		{
+			return _random.Next((int)parameters[0].Real, (int)parameters[1].Real);
+		}
+	}
+}

+ 27 - 21
NTERA.Engine/Runtime/Base/Keywords.cs

@@ -2,38 +2,36 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
 using NTERA.Engine.Compiler;
 
 namespace NTERA.Engine.Runtime.Base
 {
-	public class KeywordAttribute : Attribute
+	public static class Keywords
 	{
-		public string Name { get; }
+		private class KeywordAttribute : Attribute
+		{
+			public string Name { get; }
 
-		public bool ImplicitString { get; }
-		public bool ImplicitFormatted { get; }
+			public bool ImplicitString { get; }
+			public bool ImplicitFormatted { get; }
 
-		public KeywordAttribute(string name, bool implicitString = false, bool implicitFormatted = false)
-		{
-			if (implicitFormatted && !implicitString)
-				throw new ArgumentException("Keyword cannot support formatting if it does not use implicit strings");
+			public KeywordAttribute(string name, bool implicitString = false, bool implicitFormatted = false)
+			{
+				if (implicitFormatted && !implicitString)
+					throw new ArgumentException("Keyword cannot support formatting if it does not use implicit strings");
 
-			Name = name;
+				Name = name;
 
-			ImplicitString = implicitString;
-			ImplicitFormatted = implicitFormatted;
+				ImplicitString = implicitString;
+				ImplicitFormatted = implicitFormatted;
+			}
 		}
-	}
 
-	public static class Keywords
-	{
-		public static Dictionary<string, Action<EraRuntime, ExecutionSet, ExecutionNode>> StaticKeywords { get; } = _getKeywords();
+		public static Dictionary<string, Action<EraRuntime, StackFrame, ExecutionNode>> StaticKeywords { get; } = _getKeywords();
 
-		private static Dictionary<string, Action<EraRuntime, ExecutionSet, ExecutionNode>> _getKeywords()
+		private static Dictionary<string, Action<EraRuntime, StackFrame, ExecutionNode>> _getKeywords()
 		{
-			var output = new Dictionary<string, Action<EraRuntime, ExecutionSet, ExecutionNode>>();
+			var output = new Dictionary<string, Action<EraRuntime, StackFrame, ExecutionNode>>();
 
 			foreach (MethodInfo method in typeof(Keywords).GetMethods(BindingFlags.Public | BindingFlags.Static))
 			{
@@ -47,17 +45,25 @@ namespace NTERA.Engine.Runtime.Base
 		}
 
 		[Keyword("DRAWLINE")]
-		public static void DrawLine(EraRuntime runtime, ExecutionSet set, ExecutionNode node)
+		public static void DrawLine(EraRuntime runtime, StackFrame set, ExecutionNode node)
 		{
 			runtime.Console.PrintBar();
 		}
 
 		[Keyword("DRAWLINEFORM", true, true)]
-		public static void DrawLineForm(EraRuntime runtime, ExecutionSet set, ExecutionNode node)
+		public static void DrawLineForm(EraRuntime runtime, StackFrame set, ExecutionNode node)
 		{
 			var value = runtime.ComputeExpression(set, node.SubNodes.Single());
 
 			runtime.Console.printCustomBar(value.ToString().Trim());
 		}
+
+		[Keyword("PRINTFORML", true, true)]
+		public static void PrintLine(EraRuntime runtime, StackFrame set, ExecutionNode node)
+		{
+			var value = runtime.ComputeExpression(set, node.SubNodes.Single());
+
+			runtime.Console.PrintSingleLine(value.ToString().Trim());
+		}
 	}
 }

+ 55 - 53
NTERA.Engine/Runtime/BaseDefinitions.cs

@@ -155,59 +155,61 @@ 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("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"),
-			new FunctionDefinition("CURRENTREDRAW", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL"),
-			new FunctionDefinition("GETFONT", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL"),
-			new FunctionDefinition("GETMILLISECOND", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL")
+			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("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"),
+			new FunctionDefinition("CURRENTREDRAW", new FunctionParameter[0], new FunctionVariable[0], true, "__GLOBAL"),
+			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")
 		};
 
 		public static Keyword[] DefaultKeywords { get; } =

+ 121 - 22
NTERA.Engine/Runtime/EraRuntime.cs

@@ -12,11 +12,16 @@ namespace NTERA.Engine.Runtime
 		public IExecutionProvider ExecutionProvider { get; }
 		public IConsole Console { get; protected set; }
 
-		public Stack<ExecutionSet> ExecutionStack { get; } = new Stack<ExecutionSet>();
+		public Stack<StackFrame> ExecutionStack { get; } = new Stack<StackFrame>();
+
+		protected List<FunctionDefinition> TotalProcedureDefinitions = new List<FunctionDefinition>(BaseDefinitions.DefaultGlobalFunctions);
 
 		public EraRuntime(IExecutionProvider executionProvider)
 		{
 			ExecutionProvider = executionProvider;
+
+			TotalProcedureDefinitions.AddRange(ExecutionProvider.DefinedProcedures);
+			TotalProcedureDefinitions.AddRange(BaseDefinitions.DefaultGlobalFunctions);
 		}
 
 		public bool Initialize(IConsole console)
@@ -48,10 +53,8 @@ namespace NTERA.Engine.Runtime
 			System.Threading.Thread.Sleep(-1);
 		}
 
-		public void Call(FunctionDefinition function)
+		public ExecutionResult Call(FunctionDefinition function, IList<Value> parameters = null)
 		{
-			var executionContents = ExecutionProvider.GetExecutionNodes(function);
-
 			var localVariables = new VariableDictionary();
 
 			foreach (var variable in function.Variables)
@@ -62,32 +65,73 @@ namespace NTERA.Engine.Runtime
 			foreach (var variable in BaseDefinitions.DefaultGlobalVariables)
 				globalVariables[variable.Name] = variable.CalculatedValue;
 
-			var executionSet = new ExecutionSet
+			var context = new StackFrame
 			{
-				Nodes = executionContents.ToList(),
 				SelfDefinition = function,
 				GlobalVariables = globalVariables,
 				LocalVariables = localVariables
 			};
 
-			ExecutionStack.Push(executionSet);
+			ExecutionResult result;
+
+			ExecutionStack.Push(context);
 
-			ExecuteSet(executionSet);
+			if (function.Filename == "__GLOBAL")
+			{
+				var resultValue = Base.Functions.StaticFunctions[function.Name].Invoke(this, context, parameters);
+
+				result = new ExecutionResult(ExecutionResultType.FunctionReturn, resultValue);
+			}
+			else
+			{
+				if (parameters != null)
+					for (var index = 0; index < parameters.Count; index++)
+					{
+						FunctionParameter parameter;
+
+						if (index < function.Parameters.Length)
+							parameter = function.Parameters[index];
+						else if (index >= function.Parameters.Length && function.Parameters.Last().IsArrayParameter)
+							parameter = function.Parameters.Last();
+						else
+							throw new Exception($"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 Exception($"Unable to assign parameter '{parameter.Name}' to a variable");
+					}
+
+				var executionContents = ExecutionProvider.GetExecutionNodes(function);
+
+				result = ExecuteSet(context, executionContents.ToList());
+			}
 
 			ExecutionStack.Pop();
+
+			return result;
 		}
 
-		public void ExecuteSet(ExecutionSet set)
+		public ExecutionResult ExecuteSet(StackFrame context, IList<ExecutionNode> nodes)
 		{
-			for (; set.Position < set.Nodes.Count; set.Position++)
+			for (int index = 0; index < nodes.Count; index++)
 			{
-				ExecutionNode node = set.Nodes[set.Position];
-
-				ExecuteNode(set, node);
+				ExecutionNode node = nodes[index];
+
+				if (node.Type == "result")
+				{
+					return new ExecutionResult(ExecutionResultType.FunctionReturn, ComputeExpression(context, node.SubNodes.Single()));
+				}
+				
+				ExecuteNode(context, node);
 			}
+
+			return ExecutionResult.None;
 		}
 
-		public void ExecuteNode(ExecutionSet set, ExecutionNode node)
+		public void ExecuteNode(StackFrame context, ExecutionNode node)
 		{
 			switch (node.Type)
 			{
@@ -97,26 +141,26 @@ namespace NTERA.Engine.Runtime
 					if (!Base.Keywords.StaticKeywords.TryGetValue(statement, out var keywordAction))
 						throw new Exception($"Unknown statement: '{statement}'");
 
-					keywordAction(this, set, node);
+					keywordAction(this, context, node);
 
-					break;
+					return;
 				case "assignment":
 					string variableName = node["variable"].Metadata["name"];
 
-					if (set.LocalVariables.ContainsKey(variableName))
-						set.LocalVariables[variableName] = ComputeExpression(set, node["value"].SubNodes.Single());
-					else if (set.GlobalVariables.ContainsKey(variableName))
-						set.GlobalVariables[variableName] = ComputeExpression(set, node["value"].SubNodes.Single());
+					if (context.LocalVariables.ContainsKey(variableName))
+						context.LocalVariables[variableName] = ComputeExpression(context, node["value"].SubNodes.Single());
+					else if (context.GlobalVariables.ContainsKey(variableName))
+						context.GlobalVariables[variableName] = ComputeExpression(context, node["value"].SubNodes.Single());
 					else
 						throw new Exception($"Unknown variable: '{variableName}'");
 
-					break;
+					return;
 				default:
 					throw new Exception($"Unknown node type: '{node.Type}'");
 			}
 		}
 
-		public Value ComputeExpression(ExecutionSet set, ExecutionNode expressionNode)
+		public Value ComputeExpression(StackFrame context, ExecutionNode expressionNode)
 		{
 			switch (expressionNode.Type)
 			{
@@ -126,6 +170,61 @@ namespace NTERA.Engine.Runtime
 					string strValue = expressionNode.Metadata["value"];
 
 					return type == ValueType.String ? (Value)strValue : (Value)double.Parse(strValue);
+
+				case "variable":
+					string variableName = expressionNode.Metadata["name"];
+
+					if (context.LocalVariables.ContainsKey(variableName))
+						return context.LocalVariables[variableName];
+					else if (context.GlobalVariables.ContainsKey(variableName))
+						return context.GlobalVariables[variableName];
+					else
+						throw new Exception($"Unable to retrieve variable '{variableName}'");
+
+				case "call":
+					string functionName = expressionNode.Metadata["target"];
+					var function = TotalProcedureDefinitions.FirstOrDefault(func => func.IsReturnFunction && func.Name.Equals(functionName, StringComparison.OrdinalIgnoreCase));
+
+					if (function == null)
+						throw new Exception($"Unknown function: '{functionName}'");
+
+					var executionResult = Call(function, expressionNode["parameters"].SubNodes.Select(x => ComputeExpression(context, x)).ToArray());
+
+					if (executionResult.Type != ExecutionResultType.FunctionReturn || !executionResult.Result.HasValue)
+						throw new Exception($"Unexpected result from function '{functionName}': {executionResult.Type}");
+
+					return executionResult.Result.Value;
+
+				case "operation":
+					bool isUnary = expressionNode.Metadata.ContainsKey("unary") && bool.Parse(expressionNode.Metadata["unary"]);
+					string operationType = expressionNode.Metadata["type"];
+
+					Token operatorToken;
+
+					switch (operationType)
+					{
+						case "add": operatorToken = Token.Plus; break;
+						case "subtract": operatorToken = Token.Minus; break;
+						default: throw new Exception($"Unknown operation type: '{operationType}'");
+					}
+
+					if (isUnary)
+					{
+						Value innerValue = ComputeExpression(context, expressionNode.SubNodes.Single());
+
+						switch (operatorToken)
+						{
+							case Token.Plus: return innerValue;
+							case Token.Minus: return innerValue * -1;
+							default: throw new Exception($"Unsupported unary operation type: '{operationType}'");
+						}
+					}
+					
+					var left = ComputeExpression(context, expressionNode.SubNodes[0]);
+					var right = ComputeExpression(context, expressionNode.SubNodes[1]);
+
+					return left.Operate(right, operatorToken);
+
 				default:
 					throw new Exception($"Unknown expression type: '{expressionNode.Type}'");
 			}

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

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

+ 34 - 0
NTERA.Engine/Runtime/StackFrame.cs

@@ -0,0 +1,34 @@
+using NTERA.Engine.Compiler;
+
+namespace NTERA.Engine.Runtime
+{
+	public class StackFrame
+	{
+		public VariableDictionary LocalVariables { get; set; }
+		public VariableDictionary GlobalVariables { get; set; }
+
+		public FunctionDefinition SelfDefinition { get; set; }
+	}
+
+	public enum ExecutionResultType
+	{
+		None,
+		FunctionReturn
+	}
+
+	public class ExecutionResult
+	{
+		public static ExecutionResult None { get; } = new ExecutionResult(ExecutionResultType.None);
+
+		public ExecutionResultType Type { get; }
+
+		public Value? Result { get; }
+
+
+		public ExecutionResult(ExecutionResultType type, Value? value = null)
+		{
+			Type = type;
+			Result = value;
+		}
+	}
+}

+ 1 - 1
NTERA.Engine/Value.cs

@@ -24,7 +24,7 @@ namespace NTERA.Engine
 		{
 			Type = ValueType.Real;
 			Real = real;
-			String = real.ToString("N");
+			String = real.ToString("0.##");
 		}
 
 		public Value(string str)