plugins/STFO/STFO.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using BetterTerminal.Scripts.Language;
using BetterTerminal.Scripts.Runtime;
using HarmonyLib;
using LevelGeneration;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("STFO")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+09dcc6a36661551cc3e378f1ce513b747a2397d4")]
[assembly: AssemblyProduct("STFO")]
[assembly: AssemblyTitle("STFO")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace BetterTerminal
{
	[BepInPlugin("STFO", "STFO", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class StfoPlugin : BasePlugin
	{
		internal static ConfigFile Settings { get; private set; }

		internal static ManualLogSource ModLog { get; private set; }

		internal static ConfigEntry<bool> EnableScripts { get; private set; }

		internal static ConfigEntry<bool> AllowScriptFileWrites { get; private set; }

		internal static ConfigEntry<int> ScriptMaximumInstructions { get; private set; }

		internal static ConfigEntry<int> ScriptMaximumMemoryKB { get; private set; }

		internal static ConfigEntry<int> ScriptMaximumExecutionSeconds { get; private set; }

		internal static ConfigEntry<int> ScriptMaximumCallDepth { get; private set; }

		internal static ConfigEntry<int> ScriptMaximumImportDepth { get; private set; }

		internal static ConfigEntry<int> ScriptMaximumSourceKB { get; private set; }

		internal static ConfigEntry<int> ScriptMaximumCollectionSize { get; private set; }

		internal static ConfigEntry<int> ScriptMaximumStringLength { get; private set; }

		internal static ConfigEntry<int> MaximumInputLength { get; private set; }

		internal static ConfigEntry<int> MaximumListedScripts { get; private set; }

		internal static ConfigEntry<bool> UppercaseInput { get; private set; }

		internal static ConfigEntry<bool> ShowExecutionStatistics { get; private set; }

		internal static ConfigEntry<string> PromptPrefix { get; private set; }

		internal static ConfigEntry<string> OptionMarker { get; private set; }

		public override void Load()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			ModLog = ((BasePlugin)this).Log;
			Settings = ((BasePlugin)this).Config;
			BindConfiguration();
			ScriptWorkspace.Initialize();
			ModCommands.Register("SCRIPTS", "commands.stfo_scripts");
			ModCommands.Register("STFO", "commands.stfo_check");
			ModCommands.Register("RUN", "commands.stfo_run");
			PipelineExtensionRegistry.Register("STFO", new PipelineExtensionHandler(StfoPipelineBridge.ExecuteExtension));
			TerminalInputReservationRegistry.Register((Func<uint, bool>)StfoSessionManager.IsActive);
			new Harmony("STFO").PatchAll(typeof(StfoPlugin).Assembly);
			((BasePlugin)this).Log.LogInfo((object)"STFO loaded");
		}

		private void BindConfiguration()
		{
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Expected O, but got Unknown
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Expected O, but got Unknown
			EnableScripts = ((BasePlugin)this).Config.Bind<bool>("Scripts", "Enabled", true, "Enables STFO programs and pipeline extensions.");
			AllowScriptFileWrites = ((BasePlugin)this).Config.Bind<bool>("Scripts", "AllowFileWrites", false, "Allows WRITE operations only inside the STFO workspace.");
			ScriptMaximumInstructions = BindLimit("MaximumInstructions", 100000, 1000, 10000000);
			ScriptMaximumMemoryKB = BindLimit("MaximumMemoryKB", 4096, 256, 65536);
			ScriptMaximumExecutionSeconds = BindLimit("MaximumExecutionSeconds", 10, 1, 300);
			ScriptMaximumCallDepth = BindLimit("MaximumCallDepth", 64, 4, 1024);
			ScriptMaximumImportDepth = BindLimit("MaximumImportDepth", 16, 1, 128);
			ScriptMaximumSourceKB = BindLimit("MaximumSourceKB", 256, 8, 4096);
			ScriptMaximumCollectionSize = BindLimit("MaximumCollectionSize", 10000, 10, 1000000);
			ScriptMaximumStringLength = BindLimit("MaximumStringLength", 65536, 256, 1048576);
			MaximumInputLength = ((BasePlugin)this).Config.Bind<int>("Interface", "MaximumInputLength", 1024, new ConfigDescription("Maximum characters accepted by an interactive input request.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 65536), Array.Empty<object>()));
			MaximumListedScripts = ((BasePlugin)this).Config.Bind<int>("Interface", "MaximumListedScripts", 100, new ConfigDescription("Maximum scripts displayed by SCRIPTS.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000), Array.Empty<object>()));
			UppercaseInput = ((BasePlugin)this).Config.Bind<bool>("Interface", "UppercaseInput", false, "Converts interactive input to uppercase. Disabled preserves user text.");
			ShowExecutionStatistics = ((BasePlugin)this).Config.Bind<bool>("Interface", "ShowExecutionStatistics", true, "Shows instruction and memory usage when execution finishes.");
			PromptPrefix = ((BasePlugin)this).Config.Bind<string>("Interface", "PromptPrefix", "STFO>", "Prefix displayed for interactive input.");
			OptionMarker = ((BasePlugin)this).Config.Bind<string>("Interface", "OptionMarker", "->", "Marker displayed beside the selected option.");
		}

		private ConfigEntry<int> BindLimit(string key, int value, int minimum, int maximum)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			return ((BasePlugin)this).Config.Bind<int>("Limits", key, value, new ConfigDescription("STFO safety limit: " + key + ".", (AcceptableValueBase)(object)new AcceptableValueRange<int>(minimum, maximum), Array.Empty<object>()));
		}
	}
	internal static class ScriptWorkspace
	{
		private sealed class WorkspaceAdapter : IStfoWorkspace
		{
			string IStfoWorkspace.RootDirectory => RootDirectory;

			bool IStfoWorkspace.TryResolveScript(string scriptName, out string scriptPath, out string error, string? relativeToDirectory)
			{
				return TryResolveScript(scriptName, out scriptPath, out error, relativeToDirectory);
			}

			bool IStfoWorkspace.TryResolveFile(string requestedPath, out string filePath, out string error, string? relativeToDirectory)
			{
				return TryResolveFile(requestedPath, out filePath, out error, relativeToDirectory);
			}
		}

		internal const string LanguageName = "STFO";

		internal const string Extension = ".stfo";

		internal static string RootDirectory { get; private set; } = string.Empty;

		internal static IStfoWorkspace Service { get; } = (IStfoWorkspace)(object)new WorkspaceAdapter();

		internal static void Initialize()
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			RootDirectory = Path.GetFullPath(Path.Combine(Paths.ConfigPath, "STFO", "Scripts"));
			bool flag = default(bool);
			try
			{
				Directory.CreateDirectory(RootDirectory);
				ManualLogSource modLog = StfoPlugin.ModLog;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(33, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[WORKSPACE] READY PATH='");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(RootDirectory);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' EXISTS=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(Directory.Exists(RootDirectory));
				}
				modLog.LogInfo(val);
			}
			catch (Exception ex)
			{
				ManualLogSource modLog2 = StfoPlugin.ModLog;
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(33, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("[WORKSPACE] COULD NOT CREATE '");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(RootDirectory);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("': ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<Exception>(ex);
				}
				modLog2.LogError(val2);
			}
		}

		internal static IReadOnlyList<string> ListScripts(string? filter = null)
		{
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Expected O, but got Unknown
			if (!Directory.Exists(RootDirectory))
			{
				return Array.Empty<string>();
			}
			try
			{
				return (from path in Directory.EnumerateFiles(RootDirectory, "*.stfo", SearchOption.AllDirectories)
					select Path.GetRelativePath(RootDirectory, path) into path
					select Path.ChangeExtension(path, null) into name
					where !string.IsNullOrWhiteSpace(name)
					select name.Replace(Path.DirectorySeparatorChar, '/') into name
					where string.IsNullOrWhiteSpace(filter) || name.Contains(filter, StringComparison.OrdinalIgnoreCase)
					select name).OrderBy<string, string>((string name) => name, StringComparer.OrdinalIgnoreCase).ToArray();
			}
			catch (Exception ex)
			{
				ManualLogSource modLog = StfoPlugin.ModLog;
				bool flag = default(bool);
				BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(36, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[WORKSPACE] COULD NOT LIST SCRIPTS: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
				}
				modLog.LogWarning(val);
				return Array.Empty<string>();
			}
		}

		internal static bool TryResolveScript(string scriptName, out string scriptPath, out string error, string? relativeToDirectory = null)
		{
			string text = scriptName.Trim();
			if (!text.EndsWith(".stfo", StringComparison.OrdinalIgnoreCase))
			{
				text += ".stfo";
			}
			return TryResolveFile(text, out scriptPath, out error, relativeToDirectory);
		}

		internal static bool TryResolveFile(string requestedPath, out string filePath, out string error, string? relativeToDirectory = null)
		{
			filePath = string.Empty;
			error = string.Empty;
			if (string.IsNullOrWhiteSpace(requestedPath) || requestedPath.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
			{
				error = "INVALID FILE PATH.";
				return false;
			}
			string text = (string.IsNullOrWhiteSpace(relativeToDirectory) ? RootDirectory : Path.GetFullPath(relativeToDirectory));
			if (!IsInsideRoot(text, allowRoot: true))
			{
				error = "BASE DIRECTORY OUTSIDE SCRIPT WORKSPACE.";
				return false;
			}
			string fullPath = Path.GetFullPath(Path.Combine(text, requestedPath));
			if (!IsInsideRoot(fullPath, allowRoot: false))
			{
				error = "FILE PATH OUTSIDE SCRIPT WORKSPACE.";
				return false;
			}
			if (ContainsReparsePoint(fullPath))
			{
				error = "SYMLINKS AND JUNCTIONS ARE NOT ALLOWED IN SCRIPT PATHS.";
				return false;
			}
			filePath = fullPath;
			return true;
		}

		private static bool IsInsideRoot(string path, bool allowRoot)
		{
			string text = RootDirectory.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
			string text2 = Path.GetFullPath(path).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
			if (allowRoot && string.Equals(text2, text, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			return text2.StartsWith(text + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase);
		}

		private static bool ContainsReparsePoint(string path)
		{
			string relativePath = Path.GetRelativePath(RootDirectory, path);
			string text = RootDirectory;
			string[] array = relativePath.Split(new char[2]
			{
				Path.DirectorySeparatorChar,
				Path.AltDirectorySeparatorChar
			}, StringSplitOptions.RemoveEmptyEntries);
			foreach (string path2 in array)
			{
				text = Path.Combine(text, path2);
				if ((Directory.Exists(text) || File.Exists(text)) && (File.GetAttributes(text) & FileAttributes.ReparsePoint) != FileAttributes.None)
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class StfoIntegration
	{
		internal static StfoPolicy CreatePolicy()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			return new StfoPolicy(StfoPlugin.ScriptMaximumInstructions.Value, StfoPlugin.ScriptMaximumMemoryKB.Value, StfoPlugin.ScriptMaximumExecutionSeconds.Value, StfoPlugin.ScriptMaximumCallDepth.Value, StfoPlugin.ScriptMaximumImportDepth.Value, StfoPlugin.ScriptMaximumSourceKB.Value, StfoPlugin.ScriptMaximumCollectionSize.Value, StfoPlugin.ScriptMaximumStringLength.Value);
		}

		internal static StfoAnalyzer CreateAnalyzer(StfoPolicy policy)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			return new StfoAnalyzer(policy, ScriptWorkspace.Service);
		}

		internal static StfoVirtualMachine CreateVirtualMachine(StfoPolicy policy, IEnumerable<string>? inputs = null)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			return new StfoVirtualMachine(policy, ScriptWorkspace.Service, StfoPlugin.AllowScriptFileWrites.Value, inputs);
		}
	}
	internal enum ScriptCommand
	{
		Scripts,
		Stfo,
		Run
	}
	internal enum StfoCommand
	{
		Check,
		Config
	}
	internal enum StfoSetting
	{
		Enabled,
		Writes,
		Stats,
		UppercaseInput,
		InputLimit,
		ListLimit,
		Prompt,
		Marker
	}
	[HarmonyPatch(typeof(LG_ComputerTerminalCommandInterpreter), "EvaluateInput")]
	internal static class ScriptCommandPatch
	{
		private static bool Prefix(LG_ComputerTerminalCommandInterpreter __instance, string inputString)
		{
			string text = inputString.Trim();
			string text2 = text.ToUpperInvariant();
			string[] array = text.Split(' ', StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 0 || !Enum.TryParse<ScriptCommand>(array[0], ignoreCase: true, out var result))
			{
				return true;
			}
			if (!StfoPlugin.EnableScripts.Value)
			{
				TerminalCommandOutput.WriteImmediate(__instance, text2, (IEnumerable<string>)new string[1] { LocalizationService.Text("stfo.disabled", Array.Empty<object>()) });
				return false;
			}
			switch (result)
			{
			case ScriptCommand.Stfo:
				return HandleStfo(__instance, text2, array);
			case ScriptCommand.Run:
				return HandleRun(__instance, text2, array);
			default:
			{
				if (array.Length > 2)
				{
					return true;
				}
				IReadOnlyList<string> readOnlyList = ScriptWorkspace.ListScripts((array.Length == 2) ? array[1] : null);
				int num = Math.Min(readOnlyList.Count, StfoPlugin.MaximumListedScripts.Value);
				List<string> list = new List<string>();
				list.Add(LocalizationService.Text("stfo.directory", new object[1] { ScriptWorkspace.RootDirectory }));
				list.Add($"COUNT: {readOnlyList.Count}");
				List<string> list2 = list;
				list2.AddRange(from name in readOnlyList.Take(num)
					select name.ToUpperInvariant());
				if (num < readOnlyList.Count)
				{
					list2.Add($"... {readOnlyList.Count - num} MORE. USE SCRIPTS FILTER.");
				}
				TerminalCommandOutput.WriteImmediate(__instance, text2, (IEnumerable<string>)list2);
				return false;
			}
			}
		}

		private static bool HandleStfo(LG_ComputerTerminalCommandInterpreter interpreter, string commandLine, string[] arguments)
		{
			if (arguments.Length >= 2 && Enum.TryParse<StfoCommand>(arguments[1], ignoreCase: true, out var result) && result == StfoCommand.Config)
			{
				HandleConfig(interpreter, commandLine, arguments);
				return false;
			}
			if (arguments.Length != 3 || !Enum.TryParse<StfoCommand>(arguments[1], ignoreCase: true, out var result2) || result2 != StfoCommand.Check)
			{
				TerminalCommandOutput.WriteImmediate(interpreter, commandLine, (IEnumerable<string>)new string[1] { LocalizationService.Text("stfo.use_check", Array.Empty<object>()) });
				return false;
			}
			if (!ScriptWorkspace.TryResolveScript(arguments[2], out string scriptPath, out string error))
			{
				TerminalCommandOutput.WriteImmediate(interpreter, commandLine, (IEnumerable<string>)new string[1] { LocalizationService.Text("stfo.error", new object[1] { error }) });
				return false;
			}
			StfoAnalysisResult val = StfoIntegration.CreateAnalyzer(StfoIntegration.CreatePolicy()).Analyze(scriptPath);
			List<string> list = new List<string>
			{
				"STFO CHECK: " + arguments[2].ToUpperInvariant(),
				$"FILES: {val.Files.Count}",
				$"DIAGNOSTICS: {val.Diagnostics.Count}"
			};
			if (val.Diagnostics.Count == 0)
			{
				list.Add(LocalizationService.Text("stfo.no_problems", Array.Empty<object>()));
			}
			else
			{
				list.AddRange(val.Diagnostics.Select((StfoDiagnostic item) => item.Format()));
			}
			TerminalCommandOutput.WriteImmediate(interpreter, commandLine, (IEnumerable<string>)list);
			return false;
		}

		private static bool HandleRun(LG_ComputerTerminalCommandInterpreter interpreter, string commandLine, string[] arguments)
		{
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			if (arguments.Length != 2)
			{
				TerminalCommandOutput.WriteImmediate(interpreter, commandLine, (IEnumerable<string>)new string[1] { LocalizationService.Text("stfo.use_run", Array.Empty<object>()) });
				return false;
			}
			if (!ScriptWorkspace.TryResolveScript(arguments[1], out string scriptPath, out string error))
			{
				TerminalCommandOutput.WriteImmediate(interpreter, commandLine, (IEnumerable<string>)new string[1] { LocalizationService.Text("stfo.error", new object[1] { error }) });
				return false;
			}
			StfoPolicy policy = StfoIntegration.CreatePolicy();
			StfoAnalysisResult val = StfoIntegration.CreateAnalyzer(policy).Analyze(scriptPath);
			if (val.HasErrors)
			{
				TerminalCommandOutput.WriteImmediate(interpreter, commandLine, val.Diagnostics.Select((StfoDiagnostic item) => item.Format()));
				return false;
			}
			StfoExecutionResult val2 = StfoIntegration.CreateVirtualMachine(policy).Execute(scriptPath, (StfoEntryPointKind)0, (StfoValue?)null);
			List<string> list = new List<string>(val2.PrintedLines);
			if (val2.InputRequest != null)
			{
				TerminalCommandOutput.WriteImmediate(interpreter, commandLine, (IEnumerable<string>)list);
				StfoSessionManager.Start(interpreter, scriptPath, policy, val2, commandLine);
				StfoSessionManager.RenderCurrent(interpreter);
				return false;
			}
			StfoValue output = val2.Output;
			if ((int)((StfoValue)(ref output)).Type != 0)
			{
				list.Add(LocalizationService.Text("stfo.output", new object[1] { val2.Output }));
			}
			list.AddRange(val2.Diagnostics.Select((StfoDiagnostic item) => item.Format()));
			if (StfoPlugin.ShowExecutionStatistics.Value)
			{
				list.Add(LocalizationService.Text("stfo.finished", new object[2] { val2.Instructions, val2.EstimatedMemoryBytes }));
			}
			TerminalCommandOutput.WriteImmediate(interpreter, commandLine, (IEnumerable<string>)list);
			return false;
		}

		private static void HandleConfig(LG_ComputerTerminalCommandInterpreter interpreter, string commandLine, string[] arguments)
		{
			if (arguments.Length == 2)
			{
				WriteConfig(interpreter, commandLine, CurrentConfiguration());
				return;
			}
			if (arguments.Length == 3 && arguments[2].Equals("RELOAD", StringComparison.OrdinalIgnoreCase))
			{
				StfoPlugin.Settings.Reload();
				WriteConfig(interpreter, commandLine, CurrentConfiguration());
				return;
			}
			if (arguments.Length != 4 || !Enum.TryParse<StfoSetting>(arguments[2], ignoreCase: true, out var result))
			{
				WriteConfig(interpreter, commandLine, ConfigHelp());
				return;
			}
			string value = arguments[3];
			WriteConfig(interpreter, commandLine, result switch
			{
				StfoSetting.Enabled => SetBoolean(StfoPlugin.EnableScripts, value, result), 
				StfoSetting.Writes => SetBoolean(StfoPlugin.AllowScriptFileWrites, value, result), 
				StfoSetting.Stats => SetBoolean(StfoPlugin.ShowExecutionStatistics, value, result), 
				StfoSetting.UppercaseInput => SetBoolean(StfoPlugin.UppercaseInput, value, result), 
				StfoSetting.InputLimit => SetInteger(StfoPlugin.MaximumInputLength, value, result, 1, 65536), 
				StfoSetting.ListLimit => SetInteger(StfoPlugin.MaximumListedScripts, value, result, 1, 1000), 
				StfoSetting.Prompt => SetText(StfoPlugin.PromptPrefix, value, result), 
				StfoSetting.Marker => SetText(StfoPlugin.OptionMarker, value, result), 
				_ => ConfigHelp(), 
			});
		}

		private static string CurrentConfiguration()
		{
			return string.Join("\n", "STFO CONFIGURATION", "ENABLED: " + StfoPlugin.EnableScripts.Value.ToString().ToUpperInvariant(), "WRITES: " + StfoPlugin.AllowScriptFileWrites.Value.ToString().ToUpperInvariant(), "STATS: " + StfoPlugin.ShowExecutionStatistics.Value.ToString().ToUpperInvariant(), "UPPERCASE INPUT: " + StfoPlugin.UppercaseInput.Value.ToString().ToUpperInvariant(), $"INPUT LIMIT: {StfoPlugin.MaximumInputLength.Value}", $"LIST LIMIT: {StfoPlugin.MaximumListedScripts.Value}", "PROMPT: " + StfoPlugin.PromptPrefix.Value, "MARKER: " + StfoPlugin.OptionMarker.Value, "WORKSPACE: " + ScriptWorkspace.RootDirectory);
		}

		private static string ConfigHelp()
		{
			return string.Join("\n", "STFO CONFIG", "STFO CONFIG RELOAD", "STFO CONFIG ENABLED ON|OFF", "STFO CONFIG WRITES ON|OFF", "STFO CONFIG STATS ON|OFF", "STFO CONFIG UPPERCASEINPUT ON|OFF", "STFO CONFIG INPUTLIMIT 1-65536", "STFO CONFIG LISTLIMIT 1-1000", "STFO CONFIG PROMPT TEXT", "STFO CONFIG MARKER TEXT");
		}

		private static string SetBoolean(ConfigEntry<bool> entry, string value, StfoSetting setting)
		{
			if (!value.Equals("ON", StringComparison.OrdinalIgnoreCase) && !value.Equals("OFF", StringComparison.OrdinalIgnoreCase))
			{
				return "INVALID " + setting.ToString().ToUpperInvariant() + ". USE ON OR OFF.";
			}
			entry.Value = value.Equals("ON", StringComparison.OrdinalIgnoreCase);
			return setting.ToString().ToUpperInvariant() + ": " + (entry.Value ? "ON" : "OFF");
		}

		private static string SetInteger(ConfigEntry<int> entry, string value, StfoSetting setting, int minimum, int maximum)
		{
			if (int.TryParse(value, out var result) && result >= minimum && result <= maximum)
			{
				entry.Value = result;
				return $"{setting.ToString().ToUpperInvariant()}: {result}";
			}
			return $"INVALID {setting.ToString().ToUpperInvariant()}. USE {minimum}-{maximum}.";
		}

		private static string SetText(ConfigEntry<string> entry, string value, StfoSetting setting)
		{
			string text = new string(value.Where((char character) => !char.IsControl(character)).Take(16).ToArray());
			if (text.Length == 0)
			{
				return "INVALID " + setting.ToString().ToUpperInvariant() + ".";
			}
			entry.Value = text;
			return setting.ToString().ToUpperInvariant() + ": " + text;
		}

		private static void WriteConfig(LG_ComputerTerminalCommandInterpreter interpreter, string commandLine, string response)
		{
			TerminalCommandOutput.WriteImmediate(interpreter, commandLine, (IEnumerable<string>)response.Split('\n'));
		}
	}
}
namespace BetterTerminal.Scripts.Runtime
{
	internal sealed class StfoSession
	{
		internal string ScriptPath { get; init; } = string.Empty;

		internal StfoPolicy Policy { get; init; }

		internal List<string> Inputs { get; } = new List<string>();

		internal int PrintedLineCount { get; set; }

		internal StfoInputRequest Request { get; set; }

		internal int SelectedOption { get; set; }

		internal List<string> RenderedOptionLines { get; } = new List<string>();

		internal string TextBuffer { get; set; } = string.Empty;

		internal bool WaitingForFreshInput { get; set; } = true;

		internal string ActivationCommand { get; init; } = string.Empty;

		internal bool ActivationCommandDiscarded { get; set; }

		internal bool InputLimitReached { get; set; }
	}
	internal static class StfoSessionManager
	{
		private static readonly Dictionary<uint, StfoSession> Sessions = new Dictionary<uint, StfoSession>();

		internal static void Start(LG_ComputerTerminalCommandInterpreter interpreter, string scriptPath, StfoPolicy policy, StfoExecutionResult result, string activationCommand)
		{
			if (result.InputRequest != null && interpreter.m_terminal != null)
			{
				Sessions[interpreter.m_terminal.SyncID] = new StfoSession
				{
					ScriptPath = scriptPath,
					Policy = policy,
					PrintedLineCount = result.PrintedLines.Count,
					Request = result.InputRequest,
					ActivationCommand = activationCommand.Trim().ToUpperInvariant()
				};
				AutoCompletePatch.ClearSelection();
				PrepareInputLine(interpreter);
			}
		}

		internal static bool TryHandleInput(LG_ComputerTerminalCommandInterpreter interpreter, string input)
		{
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Invalid comparison between Unknown and I4
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Expected O, but got Unknown
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			if (interpreter.m_terminal == null || !Sessions.TryGetValue(interpreter.m_terminal.SyncID, out StfoSession value))
			{
				return false;
			}
			string text = input.Trim().ToUpperInvariant();
			bool flag = default(bool);
			if (!value.ActivationCommandDiscarded && text.Equals(value.ActivationCommand, StringComparison.OrdinalIgnoreCase))
			{
				value.ActivationCommandDiscarded = true;
				interpreter.m_terminal.m_currentLine = string.Empty;
				ManualLogSource modLog = StfoPlugin.ModLog;
				BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(52, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[INPUT] DISCARDED EVALUATEINPUT ACTIVATION REPLAY '");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'");
				}
				modLog.LogDebug(val);
				PrepareInputLine(interpreter);
				return true;
			}
			if (!TryNormalizeInput(value.Request.Kind, input, out string normalized, out string error))
			{
				TerminalCommandOutput.WriteTransient(interpreter, (IEnumerable<string>)new string[2]
				{
					"STFO INPUT ERROR: " + error,
					"> " + value.Request.Prompt
				});
				PrepareInputLine(interpreter);
				return true;
			}
			if ((int)value.Request.Kind == 3)
			{
				normalized = (value.Request.Options ?? Array.Empty<string>())[value.SelectedOption];
				value.RenderedOptionLines.Clear();
				interpreter.m_screenBuffer.Add(LocalizationService.Text("stfo.selected", new object[1] { normalized.ToUpperInvariant() }));
				interpreter.m_screenBuffer.Add(string.Empty);
				ManualLogSource modLog2 = StfoPlugin.ModLog;
				BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(39, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[INPUT] OPTION ACCEPTED INDEX=");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(value.SelectedOption);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" VALUE='");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(normalized);
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'");
				}
				modLog2.LogDebug(val);
			}
			value.ActivationCommandDiscarded = true;
			value.Inputs.Add(normalized);
			StfoExecutionResult val2 = StfoIntegration.CreateVirtualMachine(value.Policy, value.Inputs).Execute(value.ScriptPath, (StfoEntryPointKind)0, (StfoValue?)null);
			List<string> list = val2.PrintedLines.Skip(value.PrintedLineCount).ToList();
			value.PrintedLineCount = val2.PrintedLines.Count;
			if (val2.InputRequest != null)
			{
				value.Request = val2.InputRequest;
				value.SelectedOption = 0;
				value.TextBuffer = string.Empty;
				value.InputLimitReached = false;
				value.WaitingForFreshInput = true;
				TerminalCommandOutput.WriteTransient(interpreter, (IEnumerable<string>)list);
				RenderCurrent(interpreter);
				return true;
			}
			Sessions.Remove(interpreter.m_terminal.SyncID);
			ReleaseInputLine(interpreter);
			StfoValue output = val2.Output;
			if ((int)((StfoValue)(ref output)).Type != 0)
			{
				list.Add(LocalizationService.Text("stfo.output", new object[1] { val2.Output }));
			}
			list.AddRange(val2.Diagnostics.Select((StfoDiagnostic item) => item.Format()));
			if (StfoPlugin.ShowExecutionStatistics.Value)
			{
				list.Add(LocalizationService.Text("stfo.finished", new object[2] { val2.Instructions, val2.EstimatedMemoryBytes }));
			}
			TerminalCommandOutput.WriteTransient(interpreter, list.Concat(new string[1] { string.Empty }));
			return true;
		}

		internal static void RenderCurrent(LG_ComputerTerminalCommandInterpreter interpreter)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			if (interpreter.m_terminal == null || !Sessions.TryGetValue(interpreter.m_terminal.SyncID, out StfoSession value))
			{
				return;
			}
			if ((int)value.Request.Kind != 3)
			{
				TerminalCommandOutput.WriteTransient(interpreter, (IEnumerable<string>)new string[3]
				{
					"> " + value.Request.Prompt,
					LocalizationService.Text("stfo.write_below", Array.Empty<object>()),
					string.Empty
				});
				PrepareInputLine(interpreter);
				return;
			}
			RemoveOptionLines(interpreter, value);
			IReadOnlyList<string> readOnlyList = value.Request.Options ?? Array.Empty<string>();
			value.RenderedOptionLines.Add(value.Request.Prompt);
			for (int i = 0; i < readOnlyList.Count; i++)
			{
				string value2 = ((i == value.SelectedOption) ? SanitizeInterfaceToken(StfoPlugin.OptionMarker.Value, "->") : "  ");
				value.RenderedOptionLines.Add($"{value2} {i + 1}. {readOnlyList[i]}");
			}
			TerminalCommandOutput.WriteTransient(interpreter, (IEnumerable<string>)value.RenderedOptionLines);
			PrepareInputLine(interpreter);
		}

		internal static bool TryMoveOption(LG_ComputerTerminal terminal, bool previous)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Invalid comparison between Unknown and I4
			if (!Sessions.TryGetValue(terminal.SyncID, out StfoSession value) || (int)value.Request.Kind != 3)
			{
				return false;
			}
			int num = value.Request.Options?.Count ?? 0;
			if (num == 0)
			{
				return false;
			}
			value.SelectedOption = (value.SelectedOption + ((!previous) ? 1 : (-1)) + num) % num;
			RenderCurrent(terminal.m_command);
			return true;
		}

		internal static bool IsActive(uint terminalId)
		{
			return Sessions.ContainsKey(terminalId);
		}

		internal static bool TryConsumeKeyboardInput(LG_ComputerTerminal terminal)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Invalid comparison between Unknown and I4
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Expected O, but got Unknown
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Expected O, but got Unknown
			if (!Sessions.TryGetValue(terminal.SyncID, out StfoSession value) || (int)value.Request.Kind == 3)
			{
				return false;
			}
			string inputString = Input.inputString;
			if (value.WaitingForFreshInput)
			{
				if (string.IsNullOrEmpty(inputString))
				{
					value.WaitingForFreshInput = false;
					StfoPlugin.ModLog.LogDebug((object)"[INPUT] FRESH KEYBOARD INPUT ARMED");
				}
				else
				{
					ManualLogSource modLog = StfoPlugin.ModLog;
					bool flag = default(bool);
					BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(42, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[INPUT] DISCARDED ACTIVATION INPUT LENGTH=");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(inputString.Length);
					}
					modLog.LogDebug(val);
				}
				return true;
			}
			if (string.IsNullOrEmpty(inputString))
			{
				return false;
			}
			string text = inputString;
			foreach (char c in text)
			{
				bool flag;
				switch (c)
				{
				case '\b':
					if (value.TextBuffer.Length > 0)
					{
						StfoSession stfoSession = value;
						string textBuffer = value.TextBuffer;
						stfoSession.TextBuffer = textBuffer.Substring(0, textBuffer.Length - 1);
					}
					value.InputLimitReached = false;
					continue;
				case '\n':
				case '\r':
					flag = true;
					break;
				default:
					flag = false;
					break;
				}
				if (flag)
				{
					string textBuffer2 = value.TextBuffer;
					value.TextBuffer = string.Empty;
					BepInExDebugLogInterpolatedStringHandler val;
					if (!value.ActivationCommandDiscarded && textBuffer2.Equals(value.ActivationCommand, StringComparison.OrdinalIgnoreCase))
					{
						value.ActivationCommandDiscarded = true;
						ManualLogSource modLog2 = StfoPlugin.ModLog;
						val = new BepInExDebugLogInterpolatedStringHandler(48, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[INPUT] DISCARDED REPLAYED ACTIVATION COMMAND '");
							((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(textBuffer2);
							((BepInExLogInterpolatedStringHandler)val).AppendLiteral("'");
						}
						modLog2.LogDebug(val);
						UpdateCustomInputLine(terminal.m_command);
						return true;
					}
					value.ActivationCommandDiscarded = true;
					ManualLogSource modLog3 = StfoPlugin.ModLog;
					val = new BepInExDebugLogInterpolatedStringHandler(30, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[INPUT] TEXT SUBMITTED LENGTH=");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(textBuffer2.Length);
					}
					modLog3.LogDebug(val);
					TryHandleInput(terminal.m_command, textBuffer2);
					return true;
				}
				if (!char.IsControl(c) && value.TextBuffer.Length < StfoPlugin.MaximumInputLength.Value)
				{
					value.TextBuffer += (StfoPlugin.UppercaseInput.Value ? char.ToUpperInvariant(c) : c);
				}
				else if (!char.IsControl(c))
				{
					value.InputLimitReached = true;
				}
			}
			UpdateCustomInputLine(terminal.m_command);
			return true;
		}

		private static void PrepareInputLine(LG_ComputerTerminalCommandInterpreter interpreter)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			interpreter.ShowInputLine = false;
			interpreter.ShowCustomLine = true;
			interpreter.CustomLineBlinkEnabled = true;
			interpreter.m_inputBufferStep = 0;
			if (interpreter.m_terminal != null)
			{
				interpreter.m_terminal.m_currentLine = string.Empty;
			}
			UpdateCustomInputLine(interpreter);
			ManualLogSource modLog = StfoPlugin.ModLog;
			bool flag = default(bool);
			BepInExDebugLogInterpolatedStringHandler val = new BepInExDebugLogInterpolatedStringHandler(51, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[INPUT] READY TERMINAL=");
				LG_ComputerTerminal terminal = interpreter.m_terminal;
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<uint?>((terminal != null) ? new uint?(terminal.SyncID) : ((uint?)null));
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ");
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("PRIVATE_BUFFER=True CUSTOM=");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<bool>(interpreter.ShowCustomLine);
			}
			modLog.LogDebug(val);
		}

		internal static void UpdateCustomInputLine(LG_ComputerTerminalCommandInterpreter interpreter)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Invalid comparison between Unknown and I4
			if (interpreter.m_terminal != null && Sessions.TryGetValue(interpreter.m_terminal.SyncID, out StfoSession value))
			{
				string text = SanitizeInterfaceToken(StfoPlugin.PromptPrefix.Value, "STFO>");
				interpreter.CustomLineText = (((int)value.Request.Kind == 3) ? (text + " [UP/DOWN + ENTER]") : (text + " " + value.TextBuffer + (value.InputLimitReached ? " [MAX]" : string.Empty)));
			}
		}

		private static void ReleaseInputLine(LG_ComputerTerminalCommandInterpreter interpreter)
		{
			interpreter.ShowCustomLine = false;
			interpreter.CustomLineBlinkEnabled = false;
			interpreter.CustomLineText = string.Empty;
			interpreter.ShowInputLine = true;
			interpreter.m_isNewInputLine = true;
			if (interpreter.m_terminal != null)
			{
				interpreter.m_terminal.m_currentLine = string.Empty;
			}
		}

		internal static void Cancel(LG_ComputerTerminal terminal)
		{
			if (Sessions.Remove(terminal.SyncID))
			{
				ReleaseInputLine(terminal.m_command);
				TerminalCommandOutput.WriteTransient(terminal.m_command, (IEnumerable<string>)new string[1] { LocalizationService.Text("stfo.cancelled", Array.Empty<object>()) });
			}
		}

		private static void RemoveOptionLines(LG_ComputerTerminalCommandInterpreter interpreter, StfoSession session)
		{
			for (int num = session.RenderedOptionLines.Count - 1; num >= 0; num--)
			{
				int num2 = -1;
				for (int num3 = interpreter.m_screenBuffer.Count - 1; num3 >= 0; num3--)
				{
					if (string.Equals(interpreter.m_screenBuffer[num3], session.RenderedOptionLines[num], StringComparison.Ordinal))
					{
						num2 = num3;
						break;
					}
				}
				if (num2 >= 0)
				{
					interpreter.m_screenBuffer.RemoveAt(num2);
				}
			}
			session.RenderedOptionLines.Clear();
		}

		private static bool TryNormalizeInput(StfoInputKind kind, string input, out string normalized, out string error)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Invalid comparison between Unknown and I4
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			normalized = input.Trim();
			error = string.Empty;
			if ((int)kind == 0)
			{
				return true;
			}
			if ((int)kind == 3)
			{
				return true;
			}
			if ((int)kind == 1)
			{
				if (!double.TryParse(normalized, NumberStyles.Float, CultureInfo.InvariantCulture, out var _))
				{
					error = LocalizationService.Text("stfo.expected_number", Array.Empty<object>());
					return false;
				}
				return true;
			}
			if (normalized.Equals("TRUE", StringComparison.OrdinalIgnoreCase) || normalized.Equals("FALSE", StringComparison.OrdinalIgnoreCase) || normalized.Equals("ON", StringComparison.OrdinalIgnoreCase) || normalized.Equals("OFF", StringComparison.OrdinalIgnoreCase) || normalized.Equals("YES", StringComparison.OrdinalIgnoreCase) || normalized.Equals("NO", StringComparison.OrdinalIgnoreCase) || normalized.Equals("SI", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			error = LocalizationService.Text("stfo.expected_boolean", Array.Empty<object>());
			return false;
		}

		private static string SanitizeInterfaceToken(string value, string fallback)
		{
			string text = new string(value.Where((char character) => !char.IsControl(character)).Take(16).ToArray()).Trim();
			if (text.Length != 0)
			{
				return text;
			}
			return fallback;
		}
	}
	[HarmonyPatch(typeof(LG_ComputerTerminalCommandInterpreter), "EvaluateInput")]
	[HarmonyPriority(800)]
	internal static class StfoPrivateInputPatch
	{
		private static bool Prefix(LG_ComputerTerminalCommandInterpreter __instance, string inputString)
		{
			return !StfoSessionManager.TryHandleInput(__instance, inputString);
		}
	}
	[HarmonyPatch(typeof(LG_ComputerTerminal), "Update")]
	[HarmonyPriority(800)]
	internal static class StfoCancelInputPatch
	{
		private static bool Prefix(LG_ComputerTerminal __instance)
		{
			if (!StfoSessionManager.IsActive(__instance.SyncID))
			{
				return true;
			}
			if (!InputMapper.GetButtonDown.Invoke((InputAction)20, (eFocusState)8))
			{
				if (StfoSessionManager.TryConsumeKeyboardInput(__instance))
				{
					return false;
				}
				bool flag = InputMapper.GetButtonDown.Invoke((InputAction)15, (eFocusState)8);
				bool flag2 = InputMapper.GetButtonDown.Invoke((InputAction)16, (eFocusState)8);
				if (!flag && !flag2)
				{
					return true;
				}
				return !StfoSessionManager.TryMoveOption(__instance, flag);
			}
			StfoSessionManager.Cancel(__instance);
			return false;
		}

		private static void Postfix(LG_ComputerTerminal __instance)
		{
			if (StfoSessionManager.IsActive(__instance.SyncID))
			{
				StfoSessionManager.UpdateCustomInputLine(__instance.m_command);
			}
		}
	}
	internal sealed record StfoPipelineResult(bool Succeeded, List<PipelineItem> Items, IReadOnlyList<string> PrintedLines, IReadOnlyList<StfoDiagnostic> Diagnostics);
	internal static class StfoPipelineBridge
	{
		internal static PipelineExtensionResult ExecuteExtension(string[] arguments, List<PipelineItem> source)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			if (StfoPlugin.EnableScripts.Value)
			{
				if (arguments.Length == 2)
				{
					StfoPipelineResult stfoPipelineResult = Execute(arguments[1], source);
					List<string> list = new List<string>(stfoPipelineResult.PrintedLines);
					list.AddRange(stfoPipelineResult.Diagnostics.Select((StfoDiagnostic item) => item.Format()));
					return new PipelineExtensionResult(stfoPipelineResult.Succeeded, stfoPipelineResult.Items, (IReadOnlyList<string>)list);
				}
				return new PipelineExtensionResult(false, source, (IReadOnlyList<string>)new string[1] { "PIPELINE ERROR: USE: STFO SCRIPT" });
			}
			return new PipelineExtensionResult(false, source, (IReadOnlyList<string>)new string[1] { LocalizationService.Text("stfo.disabled", Array.Empty<object>()) });
		}

		internal unsafe static StfoPipelineResult Execute(string scriptName, List<PipelineItem> source)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Invalid comparison between Unknown and I4
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Expected O, but got Unknown
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Invalid comparison between Unknown and I4
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Invalid comparison between Unknown and I4
			if (!ScriptWorkspace.TryResolveScript(scriptName, out string scriptPath, out string error))
			{
				return Failure(error, source);
			}
			StfoPolicy policy = StfoIntegration.CreatePolicy();
			StfoAnalysisResult val = StfoIntegration.CreateAnalyzer(policy).Analyze(scriptPath);
			if (val.HasErrors)
			{
				return new StfoPipelineResult(Succeeded: false, source, Array.Empty<string>(), val.Diagnostics);
			}
			StfoValue value = StfoValue.FromList((IReadOnlyList<StfoValue>)source.Select(ToValue).ToArray());
			StfoExecutionResult val2 = StfoIntegration.CreateVirtualMachine(policy).Execute(scriptPath, (StfoEntryPointKind)1, (StfoValue?)value);
			if (val2.Succeeded)
			{
				StfoValue output = val2.Output;
				if ((int)((StfoValue)(ref output)).Type == 4)
				{
					Dictionary<string, PipelineItem> dictionary = source.ToDictionary<PipelineItem, string>((PipelineItem item) => item.Id, StringComparer.OrdinalIgnoreCase);
					List<PipelineItem> list = new List<PipelineItem>();
					output = val2.Output;
					foreach (StfoValue item in ((StfoValue)(ref output)).List)
					{
						StfoValue current = item;
						StfoValueType type = ((StfoValue)(ref current)).Type;
						StfoValue value2;
						string text = (((int)type == 3) ? ((StfoValue)(ref current)).Text : (((int)type != 5 || !((StfoValue)(ref current)).Map.TryGetValue("ID", out value2)) ? null : ((object)(*(StfoValue*)(&value2))/*cast due to .constrained prefix*/).ToString()));
						string text2 = text;
						if (text2 != null && dictionary.TryGetValue(text2, out var value3))
						{
							list.Add(value3);
						}
					}
					return new StfoPipelineResult(Succeeded: true, list, val2.PrintedLines, val2.Diagnostics);
				}
			}
			IReadOnlyList<StfoDiagnostic> readOnlyList2;
			if (val2.Diagnostics.Count <= 0)
			{
				IReadOnlyList<StfoDiagnostic> readOnlyList = (IReadOnlyList<StfoDiagnostic>)(object)new StfoDiagnostic[1]
				{
					new StfoDiagnostic((StfoDiagnosticCode)2103, (StfoDiagnosticSeverity)2, "PIPELINE ENTRY MUST OUTPUT A LIST.", new StfoSourceLocation(scriptName, 1, 1, 1))
				};
				readOnlyList2 = readOnlyList;
			}
			else
			{
				readOnlyList2 = val2.Diagnostics;
			}
			IReadOnlyList<StfoDiagnostic> diagnostics = readOnlyList2;
			return new StfoPipelineResult(Succeeded: false, source, val2.PrintedLines, diagnostics);
		}

		private static StfoValue ToValue(PipelineItem item)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			return StfoValue.FromMap((IReadOnlyDictionary<string, StfoValue>)new Dictionary<string, StfoValue>(StringComparer.OrdinalIgnoreCase)
			{
				["ID"] = StfoValue.FromText(item.Id),
				["TYPE"] = StfoValue.FromText(item.Type),
				["ZONE"] = StfoValue.FromText(item.Zone),
				["AREA"] = StfoValue.FromText(item.Area),
				["STATUS"] = StfoValue.FromText(item.Status)
			});
		}

		private static StfoPipelineResult Failure(string message, List<PipelineItem> source)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			return new StfoPipelineResult(Succeeded: false, source, Array.Empty<string>(), (IReadOnlyList<StfoDiagnostic>)(object)new StfoDiagnostic[1]
			{
				new StfoDiagnostic((StfoDiagnosticCode)4001, (StfoDiagnosticSeverity)2, message, new StfoSourceLocation("PIPELINE", 1, 1, 1))
			});
		}
	}
}

plugins/STFO/STFO.Language.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BetterTerminal.Scripts.Language;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("BetterTerminal")]
[assembly: InternalsVisibleTo("STFO")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("STFO.Language")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+09dcc6a36661551cc3e378f1ce513b747a2397d4")]
[assembly: AssemblyProduct("STFO.Language")]
[assembly: AssemblyTitle("STFO.Language")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
}
namespace BetterTerminal.Scripts.Runtime
{
	internal enum StfoValueType
	{
		None,
		Number,
		Boolean,
		Text,
		List,
		Map
	}
	internal readonly record struct StfoValue(StfoValueType Type, object? Raw)
	{
		internal static StfoValue None => new StfoValue(StfoValueType.None, null);

		internal double Number => (double)Raw;

		internal bool Boolean => (bool)Raw;

		internal string Text => (string)Raw;

		internal IReadOnlyList<StfoValue> List => (IReadOnlyList<StfoValue>)Raw;

		internal IReadOnlyDictionary<string, StfoValue> Map => (IReadOnlyDictionary<string, StfoValue>)Raw;

		internal int EstimatedBytes => Type switch
		{
			StfoValueType.None => 0, 
			StfoValueType.Number => 8, 
			StfoValueType.Boolean => 1, 
			StfoValueType.Text => Text.Length * 2, 
			StfoValueType.List => List.Sum((StfoValue item) => item.EstimatedBytes + IntPtr.Size), 
			StfoValueType.Map => Map.Sum<KeyValuePair<string, StfoValue>>((KeyValuePair<string, StfoValue> item) => item.Key.Length * 2 + item.Value.EstimatedBytes + IntPtr.Size), 
			_ => 0, 
		};

		internal static StfoValue FromNumber(double value)
		{
			return new StfoValue(StfoValueType.Number, value);
		}

		internal static StfoValue FromBoolean(bool value)
		{
			return new StfoValue(StfoValueType.Boolean, value);
		}

		internal static StfoValue FromText(string value)
		{
			return new StfoValue(StfoValueType.Text, value);
		}

		internal static StfoValue FromList(IReadOnlyList<StfoValue> value)
		{
			return new StfoValue(StfoValueType.List, value);
		}

		internal static StfoValue FromMap(IReadOnlyDictionary<string, StfoValue> value)
		{
			return new StfoValue(StfoValueType.Map, value);
		}

		public override string ToString()
		{
			return Type switch
			{
				StfoValueType.None => "NONE", 
				StfoValueType.Number => Number.ToString("0.###############", CultureInfo.InvariantCulture), 
				StfoValueType.Boolean => Boolean ? "TRUE" : "FALSE", 
				StfoValueType.Text => Text, 
				StfoValueType.List => "[" + string.Join(", ", List.Select((StfoValue item) => item.ToString())) + "]", 
				StfoValueType.Map => "{" + string.Join(", ", Map.Select<KeyValuePair<string, StfoValue>, string>((KeyValuePair<string, StfoValue> item) => $"{item.Key}: {item.Value}")) + "}", 
				_ => string.Empty, 
			};
		}

		[CompilerGenerated]
		private bool PrintMembers(StringBuilder builder)
		{
			builder.Append("Type = ");
			builder.Append(Type.ToString());
			builder.Append(", Raw = ");
			builder.Append(Raw);
			return true;
		}
	}
	internal sealed record StfoExecutionResult(IReadOnlyList<string> PrintedLines, StfoValue Output, IReadOnlyList<StfoDiagnostic> Diagnostics, int Instructions, int EstimatedMemoryBytes, StfoInputRequest? InputRequest)
	{
		internal bool IsWaitingForInput => (object)InputRequest != null;

		internal bool Succeeded
		{
			get
			{
				if (!IsWaitingForInput)
				{
					return Diagnostics.All((StfoDiagnostic item) => item.Severity != StfoDiagnosticSeverity.Error);
				}
				return false;
			}
		}

		[CompilerGenerated]
		private bool PrintMembers(StringBuilder builder)
		{
			RuntimeHelpers.EnsureSufficientExecutionStack();
			builder.Append("PrintedLines = ");
			builder.Append(PrintedLines);
			builder.Append(", Output = ");
			builder.Append(Output.ToString());
			builder.Append(", Diagnostics = ");
			builder.Append(Diagnostics);
			builder.Append(", Instructions = ");
			builder.Append(Instructions.ToString());
			builder.Append(", EstimatedMemoryBytes = ");
			builder.Append(EstimatedMemoryBytes.ToString());
			builder.Append(", InputRequest = ");
			builder.Append(InputRequest);
			return true;
		}
	}
	internal enum StfoInputKind
	{
		Text,
		Number,
		Boolean,
		Option
	}
	internal sealed record StfoInputRequest(StfoInputKind Kind, string Prompt, IReadOnlyList<string>? Options = null);
	internal sealed class StfoVirtualMachine
	{
		private readonly StfoPolicy _policy;

		private readonly IStfoWorkspace _workspace;

		private readonly bool _allowFileWrites;

		private readonly Dictionary<string, StfoValue> _variables = new Dictionary<string, StfoValue>(StringComparer.OrdinalIgnoreCase);

		private readonly Stack<Dictionary<string, StfoValue>> _localScopes = new Stack<Dictionary<string, StfoValue>>();

		private readonly Dictionary<string, StfoFunctionBinding> _functions = new Dictionary<string, StfoFunctionBinding>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> _executedImports = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly List<string> _printedLines = new List<string>();

		private readonly List<StfoDiagnostic> _diagnostics = new List<StfoDiagnostic>();

		private readonly Stopwatch _stopwatch = Stopwatch.StartNew();

		private StfoValue _output = StfoValue.None;

		private int _instructions;

		private int _estimatedMemoryBytes;

		private bool _stopped;

		private int _callDepth;

		private readonly Queue<string> _inputValues;

		private StfoInputRequest? _inputRequest;

		internal StfoVirtualMachine(StfoPolicy policy, IStfoWorkspace workspace, bool allowFileWrites, IEnumerable<string>? inputValues = null)
		{
			_policy = policy;
			_workspace = workspace;
			_allowFileWrites = allowFileWrites;
			_inputValues = new Queue<string>(inputValues ?? Array.Empty<string>());
		}

		internal StfoExecutionResult Execute(string entryPath, StfoEntryPointKind entryPointKind, StfoValue? pipelineInput = null)
		{
			string fullPath = Path.GetFullPath(entryPath);
			string file = Relative(fullPath);
			string source = File.ReadAllText(fullPath);
			StfoLexResult stfoLexResult = new StfoLexer(file, source).Lex();
			StfoEntryPoint entryPoint = StfoManifest.Parse(file, stfoLexResult.Tokens).GetEntryPoint(entryPointKind);
			if ((object)entryPoint == null)
			{
				ReportFatal((entryPointKind == StfoEntryPointKind.Program) ? StfoDiagnosticCode.MissingProgramEntryPoint : StfoDiagnosticCode.MissingPipelineEntryPoint, "SCRIPT DOES NOT DECLARE @" + entryPointKind.ToString().ToLowerInvariant() + ".", file, new StfoToken(StfoTokenKind.EndOfFile, string.Empty, 1, 1, 0));
			}
			else
			{
				ExecuteFile(fullPath);
				if (!_stopped)
				{
					IReadOnlyList<StfoValue> argumentValues = ((entryPointKind != StfoEntryPointKind.Pipeline) ? ((IReadOnlyList<StfoValue>)Array.Empty<StfoValue>()) : ((IReadOnlyList<StfoValue>)new StfoValue[1] { pipelineInput ?? StfoValue.None }));
					StfoValue output = InvokeFunction(entryPoint.FunctionName, argumentValues, file, entryPoint.Token);
					if (output.Type != StfoValueType.None)
					{
						_output = output;
					}
				}
			}
			return new StfoExecutionResult(_printedLines, _output, _diagnostics, _instructions, _estimatedMemoryBytes, _inputRequest);
		}

		private void ExecuteFile(string path)
		{
			if (_stopped || !_executedImports.Add(path))
			{
				return;
			}
			string text = Relative(path);
			try
			{
				string source = File.ReadAllText(path);
				StfoLexResult stfoLexResult = new StfoLexer(text, source).Lex();
				_diagnostics.AddRange(stfoLexResult.Diagnostics);
				if (stfoLexResult.Diagnostics.Any((StfoDiagnostic item) => item.Severity == StfoDiagnosticSeverity.Error))
				{
					_stopped = true;
					return;
				}
				StfoProgramSyntax stfoProgramSyntax = new StfoParser(text, stfoLexResult.Tokens).Parse();
				_diagnostics.AddRange(stfoProgramSyntax.Diagnostics);
				if (stfoProgramSyntax.Diagnostics.Any((StfoDiagnostic item) => item.Severity == StfoDiagnosticSeverity.Error))
				{
					_stopped = true;
					return;
				}
				foreach (StfoFunctionStatementSyntax item in stfoProgramSyntax.Statements.OfType<StfoFunctionStatementSyntax>())
				{
					_functions[item.Name] = new StfoFunctionBinding(item, path);
				}
				foreach (StfoStatementSyntax statement in stfoProgramSyntax.Statements)
				{
					if (_stopped || !ConsumeInstruction(statement.Token, text))
					{
						break;
					}
					ExecuteStatement(statement, path, text);
				}
			}
			catch (Exception ex)
			{
				ReportFatal(StfoDiagnosticCode.IoError, "EXECUTION FAILED: " + ex.Message, text, new StfoToken(StfoTokenKind.EndOfFile, string.Empty, 1, 1, 0));
			}
		}

		private void ExecuteStatement(StfoStatementSyntax statement, string currentPath, string currentFile)
		{
			string scriptPath;
			string error;
			if (!(statement is StfoImportStatementSyntax stfoImportStatementSyntax))
			{
				if (!(statement is StfoAssignmentStatementSyntax stfoAssignmentStatementSyntax))
				{
					if (!(statement is StfoPrintStatementSyntax stfoPrintStatementSyntax))
					{
						if (!(statement is StfoOutputStatementSyntax stfoOutputStatementSyntax))
						{
							if (!(statement is StfoExpressionStatementSyntax stfoExpressionStatementSyntax))
							{
								if (!(statement is StfoIfStatementSyntax statement2))
								{
									if (statement is StfoFunctionStatementSyntax)
									{
										return;
									}
									if (!(statement is StfoReturnStatementSyntax stfoReturnStatementSyntax))
									{
										if (!(statement is StfoForStatementSyntax statement3))
										{
											if (statement is StfoWhileStatementSyntax statement4)
											{
												ExecuteWhile(statement4, currentPath, currentFile);
											}
										}
										else
										{
											ExecuteFor(statement3, currentPath, currentFile);
										}
									}
									else
									{
										if (_callDepth != 0)
										{
											throw new StfoReturnSignal(Evaluate(stfoReturnStatementSyntax.Expression, currentFile));
										}
										ReportFatal(StfoDiagnosticCode.ReturnOutsideFunction, "RETURN USED OUTSIDE FUNCTION.", currentFile, stfoReturnStatementSyntax.Token);
									}
								}
								else
								{
									ExecuteIf(statement2, currentPath, currentFile);
								}
							}
							else
							{
								Evaluate(stfoExpressionStatementSyntax.Expression, currentFile);
							}
						}
						else
						{
							_output = Evaluate(stfoOutputStatementSyntax.Expression, currentFile);
							UpdateMemory(stfoOutputStatementSyntax.Token, currentFile);
						}
					}
					else
					{
						_printedLines.Add(Evaluate(stfoPrintStatementSyntax.Expression, currentFile).ToString());
						UpdateMemory(stfoPrintStatementSyntax.Token, currentFile);
					}
				}
				else
				{
					SetVariable(stfoAssignmentStatementSyntax.Name, Evaluate(stfoAssignmentStatementSyntax.Expression, currentFile), stfoAssignmentStatementSyntax.Token, currentFile);
				}
			}
			else if (!_workspace.TryResolveScript(stfoImportStatementSyntax.Path, out scriptPath, out error, Path.GetDirectoryName(currentPath)))
			{
				ReportFatal(StfoDiagnosticCode.ImportOutsideWorkspace, error, currentFile, stfoImportStatementSyntax.Token);
			}
			else
			{
				ExecuteFile(scriptPath);
			}
		}

		private void ExecuteFor(StfoForStatementSyntax statement, string currentPath, string currentFile)
		{
			StfoValue stfoValue = Evaluate(statement.Collection, currentFile);
			if (stfoValue.Type != StfoValueType.List)
			{
				ReportFatal(StfoDiagnosticCode.InvalidOperation, "FOR EXPECTS A LIST.", currentFile, statement.Token);
				return;
			}
			foreach (StfoValue item in stfoValue.List)
			{
				if (_stopped || !ConsumeInstruction(statement.Token, currentFile))
				{
					break;
				}
				SetVariable(statement.Variable, item, statement.Token, currentFile);
				foreach (StfoStatementSyntax item2 in statement.Body)
				{
					if (_stopped || !ConsumeInstruction(item2.Token, currentFile))
					{
						return;
					}
					ExecuteStatement(item2, currentPath, currentFile);
				}
			}
		}

		private void ExecuteWhile(StfoWhileStatementSyntax statement, string currentPath, string currentFile)
		{
			while (!_stopped && ConsumeInstruction(statement.Token, currentFile))
			{
				StfoValue stfoValue = Evaluate(statement.Condition, currentFile);
				if (stfoValue.Type != StfoValueType.Boolean)
				{
					ReportFatal(StfoDiagnosticCode.InvalidOperation, "WHILE CONDITION MUST BE BOOLEAN.", currentFile, statement.Token);
					break;
				}
				if (!stfoValue.Boolean)
				{
					break;
				}
				foreach (StfoStatementSyntax item in statement.Body)
				{
					if (_stopped || !ConsumeInstruction(item.Token, currentFile))
					{
						return;
					}
					ExecuteStatement(item, currentPath, currentFile);
				}
			}
		}

		private void ExecuteIf(StfoIfStatementSyntax statement, string currentPath, string currentFile)
		{
			StfoValue stfoValue = Evaluate(statement.Condition, currentFile);
			if (stfoValue.Type != StfoValueType.Boolean)
			{
				ReportFatal(StfoDiagnosticCode.InvalidOperation, "IF CONDITION MUST BE BOOLEAN.", currentFile, statement.Token);
				return;
			}
			foreach (StfoStatementSyntax item in stfoValue.Boolean ? statement.ThenStatements : statement.ElseStatements)
			{
				if (_stopped || !ConsumeInstruction(item.Token, currentFile))
				{
					break;
				}
				ExecuteStatement(item, currentPath, currentFile);
			}
		}

		private StfoValue Evaluate(StfoExpressionSyntax expression, string file)
		{
			if (_stopped || !ConsumeInstruction(expression.Token, file))
			{
				return StfoValue.None;
			}
			if (!(expression is StfoLiteralExpressionSyntax stfoLiteralExpressionSyntax))
			{
				if (!(expression is StfoNameExpressionSyntax expression2))
				{
					if (!(expression is StfoUnaryExpressionSyntax expression3))
					{
						if (!(expression is StfoBinaryExpressionSyntax expression4))
						{
							if (!(expression is StfoCallExpressionSyntax call))
							{
								if (expression is StfoListExpressionSyntax expression5)
								{
									return EvaluateList(expression5, file);
								}
								return StfoValue.None;
							}
							return EvaluateCall(call, file);
						}
						return EvaluateBinary(expression4, file);
					}
					return EvaluateUnary(expression3, file);
				}
				return GetVariable(expression2, file);
			}
			return FromLiteral(stfoLiteralExpressionSyntax.Value);
		}

		private StfoValue EvaluateList(StfoListExpressionSyntax expression, string file)
		{
			if (expression.Items.Count > _policy.MaximumCollectionSize)
			{
				ReportFatal(StfoDiagnosticCode.CollectionLimit, "MAXIMUM COLLECTION SIZE EXCEEDED.", file, expression.Token);
				return StfoValue.None;
			}
			List<StfoValue> list = new List<StfoValue>(expression.Items.Count);
			foreach (StfoExpressionSyntax item in expression.Items)
			{
				list.Add(Evaluate(item, file));
			}
			return StfoValue.FromList(list);
		}

		private StfoValue EvaluateCall(StfoCallExpressionSyntax call, string file)
		{
			List<StfoValue> list = new List<StfoValue>(call.Arguments.Count);
			foreach (StfoExpressionSyntax argument in call.Arguments)
			{
				list.Add(Evaluate(argument, file));
			}
			if (_stopped)
			{
				return StfoValue.None;
			}
			return InvokeFunction(call.Name, list, file, call.Token);
		}

		private StfoValue InvokeFunction(string functionName, IReadOnlyList<StfoValue> argumentValues, string file, StfoToken callToken)
		{
			if (TryInvokeBuiltin(functionName, argumentValues, file, callToken, out var result))
			{
				return result;
			}
			if (!_functions.TryGetValue(functionName, out StfoFunctionBinding value))
			{
				ReportFatal(StfoDiagnosticCode.UnknownFunction, "UNKNOWN FUNCTION: " + functionName, file, callToken);
				return StfoValue.None;
			}
			StfoFunctionStatementSyntax syntax = value.Syntax;
			if (argumentValues.Count != syntax.Parameters.Count)
			{
				ReportFatal(StfoDiagnosticCode.ArgumentCountMismatch, $"FUNCTION {syntax.Name} EXPECTS {syntax.Parameters.Count} ARGUMENTS, RECEIVED {argumentValues.Count}.", file, callToken);
				return StfoValue.None;
			}
			if (_callDepth >= _policy.MaximumCallDepth)
			{
				ReportFatal(StfoDiagnosticCode.CallDepthLimit, "MAXIMUM CALL DEPTH EXCEEDED.", file, callToken);
				return StfoValue.None;
			}
			Dictionary<string, StfoValue> dictionary = new Dictionary<string, StfoValue>(StringComparer.OrdinalIgnoreCase);
			for (int i = 0; i < syntax.Parameters.Count; i++)
			{
				dictionary[syntax.Parameters[i]] = argumentValues[i];
			}
			_localScopes.Push(dictionary);
			_callDepth++;
			try
			{
				foreach (StfoStatementSyntax item in syntax.Body)
				{
					if (!_stopped && ConsumeInstruction(item.Token, file))
					{
						ExecuteStatement(item, value.SourcePath, Relative(value.SourcePath));
						continue;
					}
					break;
				}
			}
			catch (StfoReturnSignal stfoReturnSignal)
			{
				return stfoReturnSignal.Value;
			}
			finally
			{
				_callDepth--;
				_localScopes.Pop();
			}
			return StfoValue.None;
		}

		private bool TryInvokeBuiltin(string functionName, IReadOnlyList<StfoValue> arguments, string file, StfoToken token, out StfoValue result)
		{
			result = StfoValue.None;
			if (!Enum.TryParse<StfoBuiltinFunction>(functionName.Replace("_", string.Empty), ignoreCase: true, out var result2))
			{
				return false;
			}
			switch (result2)
			{
			case StfoBuiltinFunction.Read:
			{
				if (!RequireArguments(arguments, 1, result2, file, token))
				{
					return true;
				}
				if (arguments[0].Type != StfoValueType.Text)
				{
					result = InvalidBuiltinType(result2, file, token);
					return true;
				}
				if (!TryResolveRuntimeFile(arguments[0].Text, file, out string filePath, out string error))
				{
					ReportFatal(StfoDiagnosticCode.ImportOutsideWorkspace, error, file, token);
					return true;
				}
				if (!File.Exists(filePath))
				{
					ReportFatal(StfoDiagnosticCode.IoError, "FILE NOT FOUND.", file, token);
					return true;
				}
				string text = File.ReadAllText(filePath);
				if (text.Length > _policy.MaximumStringLength)
				{
					ReportFatal(StfoDiagnosticCode.StringLimit, "FILE CONTENT EXCEEDS MAXIMUM STRING LENGTH.", file, token);
					return true;
				}
				result = StfoValue.FromText(text);
				return true;
			}
			case StfoBuiltinFunction.Write:
			{
				if (!RequireArguments(arguments, 2, result2, file, token))
				{
					return true;
				}
				if (arguments[0].Type != StfoValueType.Text || arguments[1].Type != StfoValueType.Text)
				{
					result = InvalidBuiltinType(result2, file, token);
					return true;
				}
				if (!_allowFileWrites)
				{
					ReportFatal(StfoDiagnosticCode.FileWriteDisabled, "SCRIPT FILE WRITES ARE DISABLED.", file, token);
					return true;
				}
				if (!TryResolveRuntimeFile(arguments[0].Text, file, out string filePath2, out string error2))
				{
					ReportFatal(StfoDiagnosticCode.ImportOutsideWorkspace, error2, file, token);
					return true;
				}
				string directoryName = Path.GetDirectoryName(filePath2);
				if (!string.IsNullOrEmpty(directoryName))
				{
					Directory.CreateDirectory(directoryName);
				}
				File.WriteAllText(filePath2, arguments[1].Text);
				result = StfoValue.FromBoolean(value: true);
				return true;
			}
			case StfoBuiltinFunction.Input:
			case StfoBuiltinFunction.InputNumber:
			case StfoBuiltinFunction.InputBool:
			{
				if (!RequireArguments(arguments, 1, result2, file, token))
				{
					return true;
				}
				if (arguments[0].Type != StfoValueType.Text)
				{
					result = InvalidBuiltinType(result2, file, token);
					return true;
				}
				StfoInputKind stfoInputKind = result2 switch
				{
					StfoBuiltinFunction.InputNumber => StfoInputKind.Number, 
					StfoBuiltinFunction.InputBool => StfoInputKind.Boolean, 
					_ => StfoInputKind.Text, 
				};
				if (_inputValues.Count == 0)
				{
					_inputRequest = new StfoInputRequest(stfoInputKind, arguments[0].Text);
					_stopped = true;
					return true;
				}
				string text2 = _inputValues.Dequeue();
				result = stfoInputKind switch
				{
					StfoInputKind.Number => StfoValue.FromNumber(double.Parse(text2, CultureInfo.InvariantCulture)), 
					StfoInputKind.Boolean => StfoValue.FromBoolean(ParseBooleanInput(text2)), 
					_ => StfoValue.FromText(text2), 
				};
				return true;
			}
			case StfoBuiltinFunction.Option:
			{
				if (!RequireArguments(arguments, 2, result2, file, token))
				{
					return true;
				}
				if (arguments[0].Type != StfoValueType.Text || arguments[1].Type != StfoValueType.List || arguments[1].List.Any((StfoValue item) => item.Type != StfoValueType.Text))
				{
					result = InvalidBuiltinType(result2, file, token);
					return true;
				}
				string[] array = arguments[1].List.Select((StfoValue item) => item.Text).ToArray();
				if (array.Length == 0)
				{
					result = InvalidBuiltinType(result2, file, token);
					return true;
				}
				if (_inputValues.Count == 0)
				{
					_inputRequest = new StfoInputRequest(StfoInputKind.Option, arguments[0].Text, array);
					_stopped = true;
					return true;
				}
				result = StfoValue.FromText(_inputValues.Dequeue());
				return true;
			}
			case StfoBuiltinFunction.Count:
				if (!RequireArguments(arguments, 1, result2, file, token))
				{
					return true;
				}
				result = arguments[0].Type switch
				{
					StfoValueType.List => StfoValue.FromNumber(arguments[0].List.Count), 
					StfoValueType.Text => StfoValue.FromNumber(arguments[0].Text.Length), 
					_ => InvalidBuiltinType(result2, file, token), 
				};
				return true;
			case StfoBuiltinFunction.Take:
			{
				if (!RequireArguments(arguments, 2, result2, file, token))
				{
					return true;
				}
				if (arguments[0].Type != StfoValueType.List || arguments[1].Type != StfoValueType.Number)
				{
					result = InvalidBuiltinType(result2, file, token);
					return true;
				}
				int count = Math.Max(0, (int)arguments[1].Number);
				result = StfoValue.FromList(arguments[0].List.Take(count).ToArray());
				return true;
			}
			case StfoBuiltinFunction.Where:
			{
				if (!RequireArguments(arguments, 3, result2, file, token))
				{
					return true;
				}
				if (arguments[0].Type != StfoValueType.List || arguments[1].Type != StfoValueType.Text)
				{
					result = InvalidBuiltinType(result2, file, token);
					return true;
				}
				string field = arguments[1].Text;
				string wanted = arguments[2].ToString();
				result = StfoValue.FromList(arguments[0].List.Where((StfoValue item) => item.Type == StfoValueType.Map && item.Map.TryGetValue(field, out var value) && value.ToString().Contains(wanted, StringComparison.OrdinalIgnoreCase)).ToArray());
				return true;
			}
			case StfoBuiltinFunction.Sort:
			{
				if (!RequireArguments(arguments, 2, result2, file, token))
				{
					return true;
				}
				if (arguments[0].Type != StfoValueType.List || arguments[1].Type != StfoValueType.Text)
				{
					result = InvalidBuiltinType(result2, file, token);
					return true;
				}
				string sortField = arguments[1].Text;
				result = StfoValue.FromList(arguments[0].List.OrderBy<StfoValue, string>((StfoValue item) => (item.Type != StfoValueType.Map || !item.Map.TryGetValue(sortField, out var value)) ? string.Empty : value.ToString(), StringComparer.OrdinalIgnoreCase).ToArray());
				return true;
			}
			default:
				return false;
			}
		}

		private bool RequireArguments(IReadOnlyList<StfoValue> arguments, int expected, StfoBuiltinFunction function, string file, StfoToken token)
		{
			if (arguments.Count == expected)
			{
				return true;
			}
			ReportFatal(StfoDiagnosticCode.ArgumentCountMismatch, $"{function.ToString().ToUpperInvariant()} EXPECTS {expected} ARGUMENTS.", file, token);
			return false;
		}

		private StfoValue InvalidBuiltinType(StfoBuiltinFunction function, string file, StfoToken token)
		{
			ReportFatal(StfoDiagnosticCode.InvalidOperation, "INVALID ARGUMENT TYPE FOR " + function.ToString().ToUpperInvariant() + ".", file, token);
			return StfoValue.None;
		}

		private StfoValue EvaluateUnary(StfoUnaryExpressionSyntax expression, string file)
		{
			StfoValue result = Evaluate(expression.Operand, file);
			if (result.Type != StfoValueType.Number)
			{
				ReportFatal(StfoDiagnosticCode.InvalidOperation, "UNARY OPERATOR EXPECTS NUMBER.", file, expression.Token);
				return StfoValue.None;
			}
			if (expression.Operator != StfoTokenKind.Minus)
			{
				return result;
			}
			return StfoValue.FromNumber(0.0 - result.Number);
		}

		private StfoValue EvaluateBinary(StfoBinaryExpressionSyntax expression, string file)
		{
			StfoValue stfoValue = Evaluate(expression.Left, file);
			StfoValue stfoValue2 = Evaluate(expression.Right, file);
			if (_stopped)
			{
				return StfoValue.None;
			}
			if (expression.Operator == StfoTokenKind.Plus && (stfoValue.Type == StfoValueType.Text || stfoValue2.Type == StfoValueType.Text))
			{
				string text = stfoValue.ToString() + stfoValue2;
				if (text.Length > _policy.MaximumStringLength)
				{
					ReportFatal(StfoDiagnosticCode.StringLimit, "MAXIMUM STRING LENGTH EXCEEDED.", file, expression.Token);
					return StfoValue.None;
				}
				return StfoValue.FromText(text);
			}
			StfoTokenKind stfoTokenKind = expression.Operator;
			if ((uint)(stfoTokenKind - 19) <= 1u)
			{
				bool flag = stfoValue.Type == stfoValue2.Type && object.Equals(stfoValue.Raw, stfoValue2.Raw);
				return StfoValue.FromBoolean((expression.Operator == StfoTokenKind.EqualEqual) ? flag : (!flag));
			}
			if (stfoValue.Type != StfoValueType.Number || stfoValue2.Type != StfoValueType.Number)
			{
				ReportFatal(StfoDiagnosticCode.InvalidOperation, "BINARY OPERATOR EXPECTS NUMBERS.", file, expression.Token);
				return StfoValue.None;
			}
			if (expression.Operator == StfoTokenKind.Slash && stfoValue2.Number == 0.0)
			{
				ReportFatal(StfoDiagnosticCode.DivisionByZero, "DIVISION BY ZERO.", file, expression.Token);
				return StfoValue.None;
			}
			return expression.Operator switch
			{
				StfoTokenKind.Plus => StfoValue.FromNumber(stfoValue.Number + stfoValue2.Number), 
				StfoTokenKind.Minus => StfoValue.FromNumber(stfoValue.Number - stfoValue2.Number), 
				StfoTokenKind.Star => StfoValue.FromNumber(stfoValue.Number * stfoValue2.Number), 
				StfoTokenKind.Slash => StfoValue.FromNumber(stfoValue.Number / stfoValue2.Number), 
				StfoTokenKind.Less => StfoValue.FromBoolean(stfoValue.Number < stfoValue2.Number), 
				StfoTokenKind.LessOrEqual => StfoValue.FromBoolean(stfoValue.Number <= stfoValue2.Number), 
				StfoTokenKind.Greater => StfoValue.FromBoolean(stfoValue.Number > stfoValue2.Number), 
				StfoTokenKind.GreaterOrEqual => StfoValue.FromBoolean(stfoValue.Number >= stfoValue2.Number), 
				_ => StfoValue.None, 
			};
		}

		private StfoValue GetVariable(StfoNameExpressionSyntax expression, string file)
		{
			if (_localScopes.Count > 0 && _localScopes.Peek().TryGetValue(expression.Name, out var value))
			{
				return value;
			}
			if (_variables.TryGetValue(expression.Name, out var value2))
			{
				return value2;
			}
			ReportFatal(StfoDiagnosticCode.UndefinedVariable, "UNDEFINED VARIABLE: " + expression.Name, file, expression.Token);
			return StfoValue.None;
		}

		private void SetVariable(string name, StfoValue value, StfoToken token, string file)
		{
			if (_localScopes.Count > 0)
			{
				_localScopes.Peek()[name] = value;
			}
			else
			{
				_variables[name] = value;
			}
			UpdateMemory(token, file);
		}

		private bool ConsumeInstruction(StfoToken token, string file)
		{
			_instructions++;
			if (_instructions > _policy.MaximumInstructions)
			{
				ReportFatal(StfoDiagnosticCode.InstructionLimit, "MAXIMUM INSTRUCTION COUNT EXCEEDED.", file, token);
				return false;
			}
			if (_stopwatch.Elapsed.TotalSeconds > (double)_policy.MaximumExecutionSeconds)
			{
				ReportFatal(StfoDiagnosticCode.TimeLimit, "MAXIMUM EXECUTION TIME EXCEEDED.", file, token);
				return false;
			}
			return true;
		}

		private void UpdateMemory(StfoToken token, string file)
		{
			_estimatedMemoryBytes = _variables.Sum<KeyValuePair<string, StfoValue>>((KeyValuePair<string, StfoValue> item) => item.Key.Length * 2 + item.Value.EstimatedBytes) + _localScopes.Sum((Dictionary<string, StfoValue> scope) => scope.Sum((KeyValuePair<string, StfoValue> item) => item.Key.Length * 2 + item.Value.EstimatedBytes)) + _printedLines.Sum((string line) => line.Length * 2) + _output.EstimatedBytes;
			if (_estimatedMemoryBytes > (long)_policy.MaximumMemoryKB * 1024L)
			{
				ReportFatal(StfoDiagnosticCode.MemoryLimit, "MAXIMUM SCRIPT MEMORY EXCEEDED.", file, token);
			}
		}

		private void ReportFatal(StfoDiagnosticCode code, string message, string file, StfoToken token)
		{
			_diagnostics.Add(new StfoDiagnostic(code, StfoDiagnosticSeverity.Error, message, new StfoSourceLocation(file, token.Line, token.Column, Math.Max(1, token.Text.Length))));
			_stopped = true;
		}

		private static StfoValue FromLiteral(object? value)
		{
			if (value != null)
			{
				if (!(value is double value2))
				{
					if (!(value is bool value3))
					{
						if (value is string value4)
						{
							return StfoValue.FromText(value4);
						}
						return StfoValue.None;
					}
					return StfoValue.FromBoolean(value3);
				}
				return StfoValue.FromNumber(value2);
			}
			return StfoValue.None;
		}

		private string Relative(string path)
		{
			return Path.GetRelativePath(_workspace.RootDirectory, path).Replace(Path.DirectorySeparatorChar, '/');
		}

		private bool TryResolveRuntimeFile(string requestedPath, string currentFile, out string filePath, out string error)
		{
			string fullPath = Path.GetFullPath(Path.Combine(_workspace.RootDirectory, currentFile));
			return _workspace.TryResolveFile(requestedPath, out filePath, out error, Path.GetDirectoryName(fullPath));
		}

		private static bool ParseBooleanInput(string input)
		{
			if (!input.Equals("TRUE", StringComparison.OrdinalIgnoreCase) && !input.Equals("ON", StringComparison.OrdinalIgnoreCase) && !input.Equals("YES", StringComparison.OrdinalIgnoreCase))
			{
				return input.Equals("SI", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}
	}
	internal sealed class StfoReturnSignal : Exception
	{
		internal StfoValue Value { get; }

		internal StfoReturnSignal(StfoValue value)
		{
			Value = value;
		}
	}
	internal enum StfoBuiltinFunction
	{
		Read,
		Write,
		Input,
		InputNumber,
		InputBool,
		Option,
		Count,
		Take,
		Where,
		Sort
	}
	internal sealed record StfoFunctionBinding(StfoFunctionStatementSyntax Syntax, string SourcePath);
}
namespace BetterTerminal.Scripts.Language
{
	internal interface IStfoWorkspace
	{
		string RootDirectory { get; }

		bool TryResolveScript(string scriptName, out string scriptPath, out string error, string? relativeToDirectory = null);

		bool TryResolveFile(string requestedPath, out string filePath, out string error, string? relativeToDirectory = null);
	}
	internal sealed record StfoAnalysisResult(IReadOnlyList<StfoDiagnostic> Diagnostics, IReadOnlyList<string> Files)
	{
		internal bool HasErrors => Diagnostics.Any((StfoDiagnostic item) => item.Severity == StfoDiagnosticSeverity.Error);

		[CompilerGenerated]
		private bool PrintMembers(StringBuilder builder)
		{
			RuntimeHelpers.EnsureSufficientExecutionStack();
			builder.Append("Diagnostics = ");
			builder.Append(Diagnostics);
			builder.Append(", Files = ");
			builder.Append(Files);
			return true;
		}
	}
	internal sealed class StfoAnalyzer
	{
		private readonly StfoPolicy _policy;

		private readonly IStfoWorkspace _workspace;

		private readonly List<StfoDiagnostic> _diagnostics = new List<StfoDiagnostic>();

		private readonly HashSet<string> _visited = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> _active = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal StfoAnalyzer(StfoPolicy policy, IStfoWorkspace workspace)
		{
			_policy = policy;
			_workspace = workspace;
		}

		internal StfoAnalysisResult Analyze(string entryPath)
		{
			AnalyzeFile(Path.GetFullPath(entryPath), 0);
			return new StfoAnalysisResult(_diagnostics.OrderBy<StfoDiagnostic, string>((StfoDiagnostic item) => item.Location.File, StringComparer.OrdinalIgnoreCase).ThenBy((StfoDiagnostic item) => item.Location.Line).ThenBy((StfoDiagnostic item) => item.Location.Column)
				.ToArray(), _visited.OrderBy<string, string>((string path) => path, StringComparer.OrdinalIgnoreCase).ToArray());
		}

		private void AnalyzeFile(string path, int depth)
		{
			string text = Path.GetRelativePath(_workspace.RootDirectory, path).Replace(Path.DirectorySeparatorChar, '/');
			if (depth > _policy.MaximumImportDepth)
			{
				Add(StfoDiagnosticCode.ImportDepthExceeded, StfoDiagnosticSeverity.Error, $"IMPORT DEPTH EXCEEDS {_policy.MaximumImportDepth}.", text, 1, 1, 1);
			}
			else if (_active.Contains(path))
			{
				Add(StfoDiagnosticCode.CircularImport, StfoDiagnosticSeverity.Error, "CIRCULAR IMPORT DETECTED.", text, 1, 1, 1);
			}
			else
			{
				if (!_visited.Add(path))
				{
					return;
				}
				if (File.Exists(path))
				{
					try
					{
						long num = (long)_policy.MaximumSourceKB * 1024L;
						if (new FileInfo(path).Length > num)
						{
							Add(StfoDiagnosticCode.SourceTooLarge, StfoDiagnosticSeverity.Error, $"SOURCE EXCEEDS {_policy.MaximumSourceKB} KB.", text, 1, 1, 1);
						}
						else
						{
							string source = File.ReadAllText(path);
							StfoLexResult stfoLexResult = new StfoLexer(text, source).Lex();
							_diagnostics.AddRange(stfoLexResult.Diagnostics);
							if (!stfoLexResult.Diagnostics.Any((StfoDiagnostic item) => item.Severity == StfoDiagnosticSeverity.Error))
							{
								StfoProgramSyntax stfoProgramSyntax = new StfoParser(text, stfoLexResult.Tokens).Parse();
								_diagnostics.AddRange(stfoProgramSyntax.Diagnostics);
								StfoManifest stfoManifest = StfoManifest.Parse(text, stfoLexResult.Tokens);
								_diagnostics.AddRange(stfoManifest.Diagnostics);
								ValidateManifest(stfoManifest, stfoProgramSyntax, text);
								AnalyzeProgramSafety(stfoProgramSyntax, text);
							}
							_active.Add(path);
							AnalyzeDirectives(stfoLexResult.Tokens, text);
							AnalyzeImports(stfoLexResult.Tokens, path, text, depth);
							_active.Remove(path);
						}
						return;
					}
					catch (Exception ex)
					{
						Add(StfoDiagnosticCode.IoError, StfoDiagnosticSeverity.Error, "CANNOT READ SCRIPT: " + ex.Message, text, 1, 1, 1);
						return;
					}
				}
				Add(StfoDiagnosticCode.ImportNotFound, StfoDiagnosticSeverity.Error, "SCRIPT FILE NOT FOUND.", text, 1, 1, 1);
			}
		}

		private void AnalyzeDirectives(IReadOnlyList<StfoToken> tokens, string file)
		{
			for (int i = 0; i + 2 < tokens.Count; i++)
			{
				if (tokens[i].Kind != StfoTokenKind.At)
				{
					continue;
				}
				StfoToken stfoToken = tokens[i + 1];
				StfoToken stfoToken2 = tokens[i + 2];
				if (stfoToken.Kind == StfoTokenKind.Identifier && Enum.TryParse<StfoEntryPointKind>(stfoToken.Text, ignoreCase: true, out var _))
				{
					continue;
				}
				if (stfoToken.Kind == StfoTokenKind.Identifier && stfoToken.Text.Equals("STFO", StringComparison.OrdinalIgnoreCase) && stfoToken2.Kind == StfoTokenKind.Number)
				{
					if (stfoToken2.Text != "1")
					{
						Add(StfoDiagnosticCode.UnsupportedLanguageVersion, StfoDiagnosticSeverity.Error, "UNSUPPORTED STFO VERSION " + stfoToken2.Text + ". USE VERSION 1.", file, stfoToken2.Line, stfoToken2.Column, stfoToken2.Text.Length);
					}
					continue;
				}
				if (stfoToken.Kind != StfoTokenKind.Identifier || stfoToken2.Kind != StfoTokenKind.Number || !TryParseLimit(stfoToken.Text, out var kind) || !int.TryParse(stfoToken2.Text, out var result2))
				{
					Add(StfoDiagnosticCode.InvalidDirective, StfoDiagnosticSeverity.Error, "INVALID LIMIT DIRECTIVE.", file, stfoToken.Line, stfoToken.Column, Math.Max(1, stfoToken.Text.Length));
					continue;
				}
				int maximum = _policy.GetMaximum(kind);
				if (result2 > maximum)
				{
					Add(StfoDiagnosticCode.LimitExceedsPolicy, StfoDiagnosticSeverity.Error, $"REQUESTED {kind.ToString().ToUpperInvariant()} {result2} EXCEEDS {maximum}.", file, stfoToken2.Line, stfoToken2.Column, stfoToken2.Text.Length);
				}
			}
		}

		private void AnalyzeImports(IReadOnlyList<StfoToken> tokens, string currentPath, string currentFile, int depth)
		{
			for (int i = 0; i + 1 < tokens.Count; i++)
			{
				StfoToken stfoToken = tokens[i];
				if (stfoToken.Kind != StfoTokenKind.Identifier || !stfoToken.Text.Equals("IMPORT", StringComparison.OrdinalIgnoreCase))
				{
					continue;
				}
				StfoToken stfoToken2 = tokens[i + 1];
				if (stfoToken2.Kind == StfoTokenKind.String)
				{
					string text = Unquote(stfoToken2.Text);
					if (!_workspace.TryResolveScript(text, out string scriptPath, out string error, Path.GetDirectoryName(currentPath)))
					{
						Add(StfoDiagnosticCode.ImportOutsideWorkspace, StfoDiagnosticSeverity.Error, error, currentFile, stfoToken2.Line, stfoToken2.Column, stfoToken2.Text.Length);
					}
					else if (!File.Exists(scriptPath))
					{
						Add(StfoDiagnosticCode.ImportNotFound, StfoDiagnosticSeverity.Error, "IMPORT NOT FOUND: " + text, currentFile, stfoToken2.Line, stfoToken2.Column, stfoToken2.Text.Length);
					}
					else
					{
						AnalyzeFile(scriptPath, depth + 1);
					}
				}
			}
		}

		private static bool TryParseLimit(string text, out StfoLimitKind kind)
		{
			string text2 = text.Replace("_", string.Empty);
			if (text2.Equals("TIMEOUT", StringComparison.OrdinalIgnoreCase))
			{
				text2 = "TimeoutSeconds";
			}
			else if (text2.Equals("MEMORY", StringComparison.OrdinalIgnoreCase))
			{
				text2 = "MemoryKB";
			}
			else if (text2.Equals("CALLDEPTH", StringComparison.OrdinalIgnoreCase))
			{
				text2 = "CallDepth";
			}
			else if (text2.Equals("IMPORTDEPTH", StringComparison.OrdinalIgnoreCase))
			{
				text2 = "ImportDepth";
			}
			else if (text2.Equals("SOURCE", StringComparison.OrdinalIgnoreCase))
			{
				text2 = "SourceKB";
			}
			return Enum.TryParse<StfoLimitKind>(text2, ignoreCase: true, out kind);
		}

		private void AnalyzeProgramSafety(StfoProgramSyntax program, string file)
		{
			foreach (StfoFunctionStatementSyntax item in program.Statements.OfType<StfoFunctionStatementSyntax>())
			{
				bool num = ContainsCall(item.Body, item.Name);
				bool flag = ContainsConditional(item.Body);
				if (num && !flag)
				{
					Add(StfoDiagnosticCode.PossibleUnboundedRecursion, StfoDiagnosticSeverity.Warning, "FUNCTION " + item.Name + " CALLS ITSELF WITHOUT A DETECTABLE IF GUARD.", file, item.Token.Line, item.Token.Column, item.Token.Text.Length);
				}
			}
		}

		private void ValidateManifest(StfoManifest manifest, StfoProgramSyntax program, string file)
		{
			StfoEntryPointKind[] values = Enum.GetValues<StfoEntryPointKind>();
			for (int i = 0; i < values.Length; i++)
			{
				StfoEntryPointKind stfoEntryPointKind = values[i];
				StfoEntryPoint entryPoint = manifest.GetEntryPoint(stfoEntryPointKind);
				if ((object)entryPoint == null)
				{
					continue;
				}
				StfoFunctionStatementSyntax stfoFunctionStatementSyntax = program.Statements.OfType<StfoFunctionStatementSyntax>().FirstOrDefault((StfoFunctionStatementSyntax item) => item.Name.Equals(entryPoint.FunctionName, StringComparison.OrdinalIgnoreCase));
				if ((object)stfoFunctionStatementSyntax == null)
				{
					Add(StfoDiagnosticCode.EntryPointNotFound, StfoDiagnosticSeverity.Error, "ENTRY FUNCTION NOT FOUND: " + entryPoint.FunctionName, file, entryPoint.Token.Line, entryPoint.Token.Column, entryPoint.Token.Text.Length);
					continue;
				}
				int num = ((stfoEntryPointKind != StfoEntryPointKind.Program) ? 1 : 0);
				if (stfoFunctionStatementSyntax.Parameters.Count != num)
				{
					Add(StfoDiagnosticCode.InvalidEntryPointSignature, StfoDiagnosticSeverity.Error, $"{stfoEntryPointKind.ToString().ToUpperInvariant()} ENTRY FUNCTION MUST HAVE {num} PARAMETER(S).", file, stfoFunctionStatementSyntax.Token.Line, stfoFunctionStatementSyntax.Token.Column, stfoFunctionStatementSyntax.Token.Text.Length);
				}
			}
		}

		private static bool ContainsConditional(IEnumerable<StfoStatementSyntax> statements)
		{
			return statements.Any(delegate(StfoStatementSyntax statement)
			{
				if (statement is StfoIfStatementSyntax)
				{
					return true;
				}
				if (statement is StfoForStatementSyntax stfoForStatementSyntax)
				{
					return ContainsConditional(stfoForStatementSyntax.Body);
				}
				return statement is StfoWhileStatementSyntax stfoWhileStatementSyntax && ContainsConditional(stfoWhileStatementSyntax.Body);
			});
		}

		private static bool ContainsCall(IEnumerable<StfoStatementSyntax> statements, string functionName)
		{
			return statements.Any(delegate(StfoStatementSyntax statement)
			{
				if (statement is StfoAssignmentStatementSyntax stfoAssignmentStatementSyntax)
				{
					return ContainsCall(stfoAssignmentStatementSyntax.Expression, functionName);
				}
				if (statement is StfoPrintStatementSyntax stfoPrintStatementSyntax)
				{
					return ContainsCall(stfoPrintStatementSyntax.Expression, functionName);
				}
				if (statement is StfoOutputStatementSyntax stfoOutputStatementSyntax)
				{
					return ContainsCall(stfoOutputStatementSyntax.Expression, functionName);
				}
				if (statement is StfoExpressionStatementSyntax stfoExpressionStatementSyntax)
				{
					return ContainsCall(stfoExpressionStatementSyntax.Expression, functionName);
				}
				if (statement is StfoReturnStatementSyntax stfoReturnStatementSyntax)
				{
					return ContainsCall(stfoReturnStatementSyntax.Expression, functionName);
				}
				if (statement is StfoIfStatementSyntax stfoIfStatementSyntax)
				{
					return ContainsCall(stfoIfStatementSyntax.Condition, functionName) || ContainsCall(stfoIfStatementSyntax.ThenStatements, functionName) || ContainsCall(stfoIfStatementSyntax.ElseStatements, functionName);
				}
				if (statement is StfoForStatementSyntax stfoForStatementSyntax)
				{
					return ContainsCall(stfoForStatementSyntax.Collection, functionName) || ContainsCall(stfoForStatementSyntax.Body, functionName);
				}
				return statement is StfoWhileStatementSyntax stfoWhileStatementSyntax && (ContainsCall(stfoWhileStatementSyntax.Condition, functionName) || ContainsCall(stfoWhileStatementSyntax.Body, functionName));
			});
		}

		private static bool ContainsCall(StfoExpressionSyntax expression, string functionName)
		{
			if (!(expression is StfoCallExpressionSyntax stfoCallExpressionSyntax))
			{
				if (!(expression is StfoUnaryExpressionSyntax stfoUnaryExpressionSyntax))
				{
					if (!(expression is StfoBinaryExpressionSyntax stfoBinaryExpressionSyntax))
					{
						if (expression is StfoListExpressionSyntax stfoListExpressionSyntax)
						{
							return stfoListExpressionSyntax.Items.Any((StfoExpressionSyntax item) => ContainsCall(item, functionName));
						}
						return false;
					}
					return ContainsCall(stfoBinaryExpressionSyntax.Left, functionName) || ContainsCall(stfoBinaryExpressionSyntax.Right, functionName);
				}
				return ContainsCall(stfoUnaryExpressionSyntax.Operand, functionName);
			}
			return stfoCallExpressionSyntax.Name.Equals(functionName, StringComparison.OrdinalIgnoreCase) || stfoCallExpressionSyntax.Arguments.Any((StfoExpressionSyntax argument) => ContainsCall(argument, functionName));
		}

		private static string Unquote(string value)
		{
			if (value.Length < 2)
			{
				return value;
			}
			return value.Substring(1, value.Length - 1 - 1);
		}

		private void Add(StfoDiagnosticCode code, StfoDiagnosticSeverity severity, string message, string file, int line, int column, int length)
		{
			_diagnostics.Add(new StfoDiagnostic(code, severity, message, new StfoSourceLocation(file, line, column, length)));
		}
	}
	internal enum StfoDiagnosticSeverity
	{
		Info,
		Warning,
		Error
	}
	internal enum StfoDiagnosticCode
	{
		InvalidCharacter = 1001,
		UnterminatedString = 1002,
		InvalidNumber = 1003,
		SourceTooLarge = 1004,
		InvalidDirective = 2001,
		LimitExceedsPolicy = 2002,
		UnsupportedLanguageVersion = 2003,
		SyntaxError = 2101,
		UndefinedVariable = 2102,
		InvalidOperation = 2103,
		DivisionByZero = 2104,
		PossibleUnboundedRecursion = 2201,
		DuplicateEntryPoint = 2301,
		EntryPointNotFound = 2302,
		InvalidEntryPointSignature = 2303,
		MissingProgramEntryPoint = 2304,
		MissingPipelineEntryPoint = 2305,
		InstructionLimit = 5001,
		MemoryLimit = 5002,
		TimeLimit = 5003,
		CallDepthLimit = 5004,
		UnknownFunction = 5005,
		ArgumentCountMismatch = 5006,
		ReturnOutsideFunction = 5007,
		CollectionLimit = 5008,
		StringLimit = 5009,
		FileWriteDisabled = 5010,
		ImportNotFound = 3001,
		ImportOutsideWorkspace = 3002,
		CircularImport = 3003,
		ImportDepthExceeded = 3004,
		IoError = 4001
	}
	internal sealed record StfoSourceLocation(string File, int Line, int Column, int Length);
	internal sealed record StfoDiagnostic(StfoDiagnosticCode Code, StfoDiagnosticSeverity Severity, string Message, StfoSourceLocation Location)
	{
		internal string Format()
		{
			return $"{Severity.ToString().ToUpperInvariant()} STFO{Code:D4} {Location.File}:{Location.Line}:{Location.Column} " + Message;
		}
	}
	internal sealed class StfoLexer
	{
		private readonly string _file;

		private readonly string _source;

		private readonly List<StfoToken> _tokens = new List<StfoToken>();

		private readonly List<StfoDiagnostic> _diagnostics = new List<StfoDiagnostic>();

		private int _offset;

		private int _line = 1;

		private int _column = 1;

		private bool IsAtEnd => _offset >= _source.Length;

		private char Current
		{
			get
			{
				if (!IsAtEnd)
				{
					return _source[_offset];
				}
				return '\0';
			}
		}

		internal StfoLexer(string file, string source)
		{
			_file = file;
			_source = source;
		}

		internal StfoLexResult Lex()
		{
			while (!IsAtEnd)
			{
				char current = Current;
				if ((current == '\t' || current == '\r' || current == ' ') ? true : false)
				{
					Advance();
					continue;
				}
				switch (current)
				{
				case '#':
					SkipComment();
					continue;
				case '\n':
					AddSingle(StfoTokenKind.NewLine);
					_line++;
					_column = 1;
					continue;
				}
				if (IsIdentifierStart(current))
				{
					ReadIdentifier();
				}
				else if (char.IsDigit(current))
				{
					ReadNumber();
				}
				else if ((current == '"' || current == '\'') ? true : false)
				{
					ReadString(current);
				}
				else
				{
					ReadSymbol();
				}
			}
			_tokens.Add(new StfoToken(StfoTokenKind.EndOfFile, string.Empty, _line, _column, _offset));
			return new StfoLexResult(_tokens, _diagnostics);
		}

		private void ReadIdentifier()
		{
			int offset = _offset;
			int line = _line;
			int column = _column;
			while (!IsAtEnd && IsIdentifierPart(Current))
			{
				Advance();
			}
			AddToken(StfoTokenKind.Identifier, offset, line, column);
		}

		private void ReadNumber()
		{
			int offset = _offset;
			int line = _line;
			int column = _column;
			while (!IsAtEnd && char.IsDigit(Current))
			{
				Advance();
			}
			if (!IsAtEnd && Current == '.' && char.IsDigit(Peek(1)))
			{
				Advance();
				while (!IsAtEnd && char.IsDigit(Current))
				{
					Advance();
				}
			}
			string source = _source;
			int num = offset;
			string text = source.Substring(num, _offset - num);
			if (!double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var _))
			{
				AddDiagnostic(StfoDiagnosticCode.InvalidNumber, StfoDiagnosticSeverity.Error, "INVALID NUMBER '" + text + "'.", line, column, text.Length);
			}
			_tokens.Add(new StfoToken(StfoTokenKind.Number, text, line, column, offset));
		}

		private void ReadString(char quote)
		{
			int offset = _offset;
			int line = _line;
			int column = _column;
			Advance();
			bool flag = false;
			while (!IsAtEnd && Current != '\n')
			{
				if (Current == '\\' && Peek(1) != 0)
				{
					Advance();
					Advance();
					continue;
				}
				if (Current == quote)
				{
					Advance();
					flag = true;
					break;
				}
				Advance();
			}
			if (!flag)
			{
				AddDiagnostic(StfoDiagnosticCode.UnterminatedString, StfoDiagnosticSeverity.Error, "UNTERMINATED STRING.", line, column, Math.Max(1, _offset - offset));
			}
			AddToken(StfoTokenKind.String, offset, line, column);
		}

		private void ReadSymbol()
		{
			int line = _line;
			int column = _column;
			StfoTokenKind? stfoTokenKind;
			switch (Current)
			{
			case '@':
				stfoTokenKind = StfoTokenKind.At;
				break;
			case '(':
				stfoTokenKind = StfoTokenKind.LeftParenthesis;
				break;
			case ')':
				stfoTokenKind = StfoTokenKind.RightParenthesis;
				break;
			case '[':
				stfoTokenKind = StfoTokenKind.LeftBracket;
				break;
			case ']':
				stfoTokenKind = StfoTokenKind.RightBracket;
				break;
			case ',':
				stfoTokenKind = StfoTokenKind.Comma;
				break;
			case ':':
				stfoTokenKind = StfoTokenKind.Colon;
				break;
			case '.':
				stfoTokenKind = StfoTokenKind.Dot;
				break;
			case '|':
				stfoTokenKind = StfoTokenKind.Pipe;
				break;
			case '+':
				stfoTokenKind = StfoTokenKind.Plus;
				break;
			case '-':
				stfoTokenKind = StfoTokenKind.Minus;
				break;
			case '*':
				stfoTokenKind = StfoTokenKind.Star;
				break;
			case '/':
				stfoTokenKind = StfoTokenKind.Slash;
				break;
			case '=':
				stfoTokenKind = ((Peek(1) != '=') ? new StfoTokenKind?(StfoTokenKind.Assign) : new StfoTokenKind?(StfoTokenKind.EqualEqual));
				break;
			case '!':
				if (Peek(1) == '=')
				{
					stfoTokenKind = StfoTokenKind.NotEqual;
					break;
				}
				goto default;
			case '<':
				stfoTokenKind = ((Peek(1) != '=') ? new StfoTokenKind?(StfoTokenKind.Less) : new StfoTokenKind?(StfoTokenKind.LessOrEqual));
				break;
			case '>':
				stfoTokenKind = ((Peek(1) != '=') ? new StfoTokenKind?(StfoTokenKind.Greater) : new StfoTokenKind?(StfoTokenKind.GreaterOrEqual));
				break;
			default:
				stfoTokenKind = null;
				break;
			}
			StfoTokenKind? stfoTokenKind2 = stfoTokenKind;
			if (!stfoTokenKind2.HasValue)
			{
				AddDiagnostic(StfoDiagnosticCode.InvalidCharacter, StfoDiagnosticSeverity.Error, $"INVALID CHARACTER '{Current}'.", line, column, 1);
				Advance();
				return;
			}
			bool flag;
			switch (stfoTokenKind2)
			{
			case StfoTokenKind.EqualEqual:
			case StfoTokenKind.NotEqual:
			case StfoTokenKind.LessOrEqual:
			case StfoTokenKind.GreaterOrEqual:
				flag = true;
				break;
			default:
				flag = false;
				break;
			}
			int num = ((!flag) ? 1 : 2);
			int offset = _offset;
			for (int i = 0; i < num; i++)
			{
				Advance();
			}
			_tokens.Add(new StfoToken(stfoTokenKind2.Value, _source.Substring(offset, num), line, column, offset));
		}

		private void AddSingle(StfoTokenKind kind)
		{
			int offset = _offset;
			int line = _line;
			int column = _column;
			Advance();
			_tokens.Add(new StfoToken(kind, _source[offset].ToString(), line, column, offset));
		}

		private void AddToken(StfoTokenKind kind, int start, int line, int column)
		{
			_tokens.Add(new StfoToken(kind, _source.Substring(start, _offset - start), line, column, start));
		}

		private void SkipComment()
		{
			while (!IsAtEnd && Current != '\n')
			{
				Advance();
			}
		}

		private void AddDiagnostic(StfoDiagnosticCode code, StfoDiagnosticSeverity severity, string message, int line, int column, int length)
		{
			_diagnostics.Add(new StfoDiagnostic(code, severity, message, new StfoSourceLocation(_file, line, column, length)));
		}

		private char Peek(int distance)
		{
			if (_offset + distance < _source.Length)
			{
				return _source[_offset + distance];
			}
			return '\0';
		}

		private void Advance()
		{
			_offset++;
			_column++;
		}

		private static bool IsIdentifierStart(char value)
		{
			if (!char.IsLetter(value))
			{
				return value == '_';
			}
			return true;
		}

		private static bool IsIdentifierPart(char value)
		{
			if (!char.IsLetterOrDigit(value))
			{
				return value == '_';
			}
			return true;
		}
	}
	internal enum StfoEntryPointKind
	{
		Program,
		Pipeline
	}
	internal sealed record StfoEntryPoint(StfoEntryPointKind Kind, string FunctionName, StfoToken Token);
	internal sealed record StfoManifest(StfoEntryPoint? Program, StfoEntryPoint? Pipeline, IReadOnlyList<StfoDiagnostic> Diagnostics)
	{
		internal StfoEntryPoint? GetEntryPoint(StfoEntryPointKind kind)
		{
			return kind switch
			{
				StfoEntryPointKind.Program => Program, 
				StfoEntryPointKind.Pipeline => Pipeline, 
				_ => throw new ArgumentOutOfRangeException("kind"), 
			};
		}

		internal static StfoManifest Parse(string file, IReadOnlyList<StfoToken> tokens)
		{
			StfoEntryPoint stfoEntryPoint = null;
			StfoEntryPoint stfoEntryPoint2 = null;
			List<StfoDiagnostic> list = new List<StfoDiagnostic>();
			for (int i = 0; i + 2 < tokens.Count; i++)
			{
				if (tokens[i].Kind == StfoTokenKind.At && tokens[i + 1].Kind == StfoTokenKind.Identifier && tokens[i + 2].Kind == StfoTokenKind.Identifier && Enum.TryParse<StfoEntryPointKind>(tokens[i + 1].Text, ignoreCase: true, out var result))
				{
					StfoEntryPoint stfoEntryPoint3 = new StfoEntryPoint(result, tokens[i + 2].Text, tokens[i + 1]);
					if (result switch
					{
						StfoEntryPointKind.Program => stfoEntryPoint, 
						StfoEntryPointKind.Pipeline => stfoEntryPoint2, 
						_ => null, 
					} != null)
					{
						list.Add(new StfoDiagnostic(StfoDiagnosticCode.DuplicateEntryPoint, StfoDiagnosticSeverity.Error, "DUPLICATE " + result.ToString().ToUpperInvariant() + " ENTRY POINT.", new StfoSourceLocation(file, tokens[i + 1].Line, tokens[i + 1].Column, tokens[i + 1].Text.Length)));
					}
					else if (result == StfoEntryPointKind.Program)
					{
						stfoEntryPoint = stfoEntryPoint3;
					}
					else
					{
						stfoEntryPoint2 = stfoEntryPoint3;
					}
				}
			}
			return new StfoManifest(stfoEntryPoint, stfoEntryPoint2, list);
		}
	}
	internal sealed class StfoParser
	{
		private readonly string _file;

		private readonly IReadOnlyList<StfoToken> _tokens;

		private readonly List<StfoDiagnostic> _diagnostics = new List<StfoDiagnostic>();

		private int _position;

		private StfoToken Current => Peek(0);

		internal StfoParser(string file, IReadOnlyList<StfoToken> tokens)
		{
			_file = file;
			_tokens = tokens;
		}

		internal StfoProgramSyntax Parse()
		{
			return new StfoProgramSyntax(ParseStatementList(), _diagnostics);
		}

		private IReadOnlyList<StfoStatementSyntax> ParseStatementList(params string[] stopKeywords)
		{
			List<StfoStatementSyntax> list = new List<StfoStatementSyntax>();
			while (Current.Kind != StfoTokenKind.EndOfFile)
			{
				SkipNewLines();
				if (Current.Kind == StfoTokenKind.EndOfFile || stopKeywords.Any(IsKeyword))
				{
					break;
				}
				if (Current.Kind == StfoTokenKind.At)
				{
					SkipLine();
					continue;
				}
				StfoStatementSyntax stfoStatementSyntax = ParseStatement();
				if ((object)stfoStatementSyntax != null)
				{
					list.Add(stfoStatementSyntax);
				}
				StfoTokenKind kind = Current.Kind;
				if ((uint)kind > 1u)
				{
					Report(Current, "EXPECTED END OF LINE.");
					SkipLine();
				}
			}
			return list;
		}

		private StfoStatementSyntax? ParseStatement()
		{
			if (IsKeyword("IMPORT"))
			{
				return ParseImport();
			}
			if (IsKeyword("PRINT"))
			{
				return ParseCommandExpression(isOutput: false);
			}
			if (IsKeyword("OUTPUT"))
			{
				return ParseCommandExpression(isOutput: true);
			}
			if (IsKeyword("IF"))
			{
				return ParseIf();
			}
			if (IsKeyword("FUNCTION"))
			{
				return ParseFunction();
			}
			if (IsKeyword("RETURN"))
			{
				return ParseReturn();
			}
			if (IsKeyword("FOR"))
			{
				return ParseFor();
			}
			if (IsKeyword("WHILE"))
			{
				return ParseWhile();
			}
			if (Current.Kind == StfoTokenKind.Identifier && Peek(1).Kind == StfoTokenKind.Assign)
			{
				StfoToken stfoToken = NextToken();
				NextToken();
				return new StfoAssignmentStatementSyntax(Expression: ParseExpression(), Token: stfoToken, Name: stfoToken.Text);
			}
			StfoExpressionSyntax stfoExpressionSyntax = ParseExpression();
			return new StfoExpressionStatementSyntax(stfoExpressionSyntax.Token, stfoExpressionSyntax);
		}

		private StfoStatementSyntax ParseIf()
		{
			StfoToken stfoToken = NextToken();
			StfoExpressionSyntax condition = ParseExpression();
			RequireBlockLineEnd();
			IReadOnlyList<StfoStatementSyntax> thenStatements = ParseStatementList("ELSE", "END");
			IReadOnlyList<StfoStatementSyntax> elseStatements = Array.Empty<StfoStatementSyntax>();
			if (IsKeyword("ELSE"))
			{
				NextToken();
				RequireBlockLineEnd();
				elseStatements = ParseStatementList("END");
			}
			RequireEnd(stfoToken);
			return new StfoIfStatementSyntax(stfoToken, condition, thenStatements, elseStatements);
		}

		private StfoStatementSyntax ParseFunction()
		{
			StfoToken stfoToken = NextToken();
			if (Current.Kind != StfoTokenKind.Identifier)
			{
				Report(Current, "FUNCTION EXPECTS A NAME.");
				SkipLine();
				return new StfoFunctionStatementSyntax(stfoToken, "INVALID", Array.Empty<string>(), Array.Empty<StfoStatementSyntax>());
			}
			string text = NextToken().Text;
			List<string> list = new List<string>();
			if (!Match(StfoTokenKind.LeftParenthesis))
			{
				Report(Current, "FUNCTION EXPECTS '('.");
			}
			do
			{
				StfoTokenKind kind = Current.Kind;
				if (((uint)kind <= 1u || kind == StfoTokenKind.RightParenthesis) ? true : false)
				{
					break;
				}
				if (Current.Kind != StfoTokenKind.Identifier)
				{
					Report(Current, "EXPECTED PARAMETER NAME.");
					break;
				}
				list.Add(NextToken().Text);
			}
			while (Match(StfoTokenKind.Comma));
			if (!Match(StfoTokenKind.RightParenthesis))
			{
				Report(Current, "FUNCTION EXPECTS ')'.");
			}
			RequireBlockLineEnd();
			IReadOnlyList<StfoStatementSyntax> body = ParseStatementList("END");
			RequireEnd(stfoToken);
			return new StfoFunctionStatementSyntax(stfoToken, text, list, body);
		}

		private StfoStatementSyntax ParseReturn()
		{
			return new StfoReturnStatementSyntax(NextToken(), ParseExpression());
		}

		private StfoStatementSyntax ParseFor()
		{
			StfoToken stfoToken = NextToken();
			if (Current.Kind != StfoTokenKind.Identifier)
			{
				Report(Current, "FOR EXPECTS A VARIABLE NAME.");
				SkipLine();
				return new StfoForStatementSyntax(stfoToken, "INVALID", new StfoLiteralExpressionSyntax(stfoToken, null), Array.Empty<StfoStatementSyntax>());
			}
			string text = NextToken().Text;
			if (!IsKeyword("IN"))
			{
				Report(Current, "FOR EXPECTS IN.");
			}
			else
			{
				NextToken();
			}
			StfoExpressionSyntax collection = ParseExpression();
			RequireBlockLineEnd();
			IReadOnlyList<StfoStatementSyntax> body = ParseStatementList("END");
			RequireEnd(stfoToken);
			return new StfoForStatementSyntax(stfoToken, text, collection, body);
		}

		private StfoStatementSyntax ParseWhile()
		{
			StfoToken stfoToken = NextToken();
			StfoExpressionSyntax condition = ParseExpression();
			RequireBlockLineEnd();
			IReadOnlyList<StfoStatementSyntax> body = ParseStatementList("END");
			RequireEnd(stfoToken);
			return new StfoWhileStatementSyntax(stfoToken, condition, body);
		}

		private StfoStatementSyntax? ParseImport()
		{
			StfoToken token = NextToken();
			if (Current.Kind != StfoTokenKind.String)
			{
				Report(Current, "IMPORT EXPECTS A STRING PATH.");
				return null;
			}
			return new StfoImportStatementSyntax(token, UnescapeString(NextToken().Text));
		}

		private StfoStatementSyntax ParseCommandExpression(bool isOutput)
		{
			StfoToken token = NextToken();
			bool num = Match(StfoTokenKind.LeftParenthesis);
			StfoExpressionSyntax expression = ParseExpression();
			if (num && !Match(StfoTokenKind.RightParenthesis))
			{
				Report(Current, "EXPECTED ')'.");
			}
			if (!isOutput)
			{
				return new StfoPrintStatementSyntax(token, expression);
			}
			return new StfoOutputStatementSyntax(token, expression);
		}

		private StfoExpressionSyntax ParseExpression(int parentPrecedence = 0)
		{
			int unaryPrecedence = GetUnaryPrecedence(Current.Kind);
			StfoExpressionSyntax stfoExpressionSyntax;
			if (unaryPrecedence > 0)
			{
				StfoToken stfoToken = NextToken();
				stfoExpressionSyntax = new StfoUnaryExpressionSyntax(Operand: ParseExpression(unaryPrecedence), Token: stfoToken, Operator: stfoToken.Kind);
			}
			else
			{
				stfoExpressionSyntax = ParsePrimaryExpression();
			}
			while (true)
			{
				int binaryPrecedence = GetBinaryPrecedence(Current.Kind);
				if (binaryPrecedence == 0 || binaryPrecedence <= parentPrecedence)
				{
					break;
				}
				StfoToken stfoToken2 = NextToken();
				StfoExpressionSyntax right = ParseExpression(binaryPrecedence);
				stfoExpressionSyntax = new StfoBinaryExpressionSyntax(stfoToken2, stfoExpressionSyntax, stfoToken2.Kind, right);
			}
			return stfoExpressionSyntax;
		}

		private StfoExpressionSyntax ParsePrimaryExpression()
		{
			if (Current.Kind == StfoTokenKind.LeftBracket)
			{
				return ParseListExpression();
			}
			if (Match(StfoTokenKind.LeftParenthesis))
			{
				StfoExpressionSyntax result = ParseExpression();
				if (!Match(StfoTokenKind.RightParenthesis))
				{
					Report(Current, "EXPECTED ')'.");
				}
				return result;
			}
			StfoToken stfoToken = NextToken();
			if (stfoToken.Kind == StfoTokenKind.Number)
			{
				double.TryParse(stfoToken.Text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2);
				return new StfoLiteralExpressionSyntax(stfoToken, result2);
			}
			if (stfoToken.Kind == StfoTokenKind.String)
			{
				return new StfoLiteralExpressionSyntax(stfoToken, UnescapeString(stfoToken.Text));
			}
			if (stfoToken.Kind == StfoTokenKind.Identifier)
			{
				if (stfoToken.Text.Equals("TRUE", StringComparison.OrdinalIgnoreCase))
				{
					return new StfoLiteralExpressionSyntax(stfoToken, true);
				}
				if (stfoToken.Text.Equals("FALSE", StringComparison.OrdinalIgnoreCase))
				{
					return new StfoLiteralExpressionSyntax(stfoToken, false);
				}
				if (stfoToken.Text.Equals("NONE", StringComparison.OrdinalIgnoreCase))
				{
					return new StfoLiteralExpressionSyntax(stfoToken, null);
				}
				if (Match(StfoTokenKind.LeftParenthesis))
				{
					List<StfoExpressionSyntax> list = new List<StfoExpressionSyntax>();
					do
					{
						StfoTokenKind kind = Current.Kind;
						if (((uint)kind <= 1u || kind == StfoTokenKind.RightParenthesis) ? true : false)
						{
							break;
						}
						list.Add(ParseExpression());
					}
					while (Match(StfoTokenKind.Comma));
					if (!Match(StfoTokenKind.RightParenthesis))
					{
						Report(Current, "EXPECTED ')' AFTER ARGUMENTS.");
					}
					return new StfoCallExpressionSyntax(stfoToken, stfoToken.Text, list);
				}
				return new StfoNameExpressionSyntax(stfoToken, stfoToken.Text);
			}
			Report(stfoToken, $"EXPECTED EXPRESSION, FOUND {stfoToken.Kind}.");
			return new StfoLiteralExpressionSyntax(stfoToken, null);
		}

		private StfoExpressionSyntax ParseListExpression()
		{
			StfoToken token = NextToken();
			List<StfoExpressionSyntax> list = new List<StfoExpressionSyntax>();
			do
			{
				StfoTokenKind kind = Current.Kind;
				if (((uint)kind <= 1u || kind == StfoTokenKind.RightBracket) ? true : false)
				{
					break;
				}
				list.Add(ParseExpression());
			}
			while (Match(StfoTokenKind.Comma));
			if (!Match(StfoTokenKind.RightBracket))
			{
				Report(Current, "EXPECTED ']'.");
			}
			return new StfoListExpressionSyntax(token, list);
		}

		private static int GetUnaryPrecedence(StfoTokenKind kind)
		{
			if ((uint)(kind - 15) <= 1u)
			{
				return 6;
			}
			return 0;
		}

		private static int GetBinaryPrecedence(StfoTokenKind kind)
		{
			switch (kind)
			{
			case StfoTokenKind.Star:
			case StfoTokenKind.Slash:
				return 5;
			case StfoTokenKind.Plus:
			case StfoTokenKind.Minus:
				return 4;
			case StfoTokenKind.Less:
			case StfoTokenKind.LessOrEqual:
			case StfoTokenKind.Greater:
			case StfoTokenKind.GreaterOrEqual:
				return 3;
			case StfoTokenKind.EqualEqual:
			case StfoTokenKind.NotEqual:
				return 2;
			default:
				return 0;
			}
		}

		private bool IsKeyword(string keyword)
		{
			if (Current.Kind == StfoTokenKind.Identifier)
			{
				return Current.Text.Equals(keyword, StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private bool Match(StfoTokenKind kind)
		{
			if (Current.Kind != kind)
			{
				return false;
			}
			NextToken();
			return true;
		}

		private void SkipNewLines()
		{
			while (Current.Kind == StfoTokenKind.NewLine)
			{
				NextToken();
			}
		}

		private void SkipLine()
		{
			while (true)
			{
				StfoTokenKind kind = Current.Kind;
				if ((uint)kind > 1u)
				{
					NextToken();
					continue;
				}
				break;
			}
		}

		private void RequireBlockLineEnd()
		{
			if (Current.Kind == StfoTokenKind.NewLine)
			{
				NextToken();
			}
			else if (Current.Kind != StfoTokenKind.EndOfFile)
			{
				Report(Current, "EXPECTED END OF LINE BEFORE BLOCK.");
				SkipLine();
				if (Current.Kind == StfoTokenKind.NewLine)
				{
					NextToken();
				}
			}
		}

		private void RequireEnd(StfoToken openingToken)
		{
			SkipNewLines();
			if (!IsKeyword("END"))
			{
				Report(Current, $"EXPECTED END FOR BLOCK STARTED AT LINE {openingToken.Line}.");
			}
			else
			{
				NextToken();
			}
		}

		private StfoToken NextToken()
		{
			StfoToken current = Current;
			if (_position < _tokens.Count - 1)
			{
				_position++;
			}
			return current;
		}

		private StfoToken Peek(int offset)
		{
			int index = Math.Min(_position + offset, _tokens.Count - 1);
			return _tokens[index];
		}

		private void Report(StfoToken token, string message)
		{
			_diagnostics.Add(new StfoDiagnostic(StfoDiagnosticCode.SyntaxError, StfoDiagnosticSeverity.Error, message, new StfoSourceLocation(_file, token.Line, token.Column, Math.Max(1, token.Text.Length))));
		}

		private static string UnescapeString(string text)
		{
			if (text.Length < 2)
			{
				return text;
			}
			StringBuilder stringBuilder = new StringBuilder(text.Length - 2);
			for (int i = 1; i < text.Length - 1; i++)
			{
				if (text[i] != '\\' || i + 1 >= text.Length - 1)
				{
					stringBuilder.Append(text[i]);
					continue;
				}
				i++;
				StringBuilder stringBuilder2 = stringBuilder;
				stringBuilder2.Append(text[i] switch
				{
					'n' => '\n', 
					'r' => '\r', 
					't' => '\t', 
					'\\' => '\\', 
					'"' => '"', 
					'\'' => '\'', 
					_ => text[i], 
				});
			}
			return stringBuilder.ToString();
		}
	}
	internal enum StfoLimitKind
	{
		Instructions,
		MemoryKB,
		TimeoutSeconds,
		CallDepth,
		ImportDepth,
		SourceKB,
		CollectionSize,
		StringLength
	}
	internal sealed record StfoPolicy(int MaximumInstructions, int MaximumMemoryKB, int MaximumExecutionSeconds, int MaximumCallDepth, int MaximumImportDepth, int MaximumSourceKB, int MaximumCollectionSize, int MaximumStringLength)
	{
		internal int GetMaximum(StfoLimitKind kind)
		{
			return kind switch
			{
				StfoLimitKind.Instructions => MaximumInstructions, 
				StfoLimitKind.MemoryKB => MaximumMemoryKB, 
				StfoLimitKind.TimeoutSeconds => MaximumExecutionSeconds, 
				StfoLimitKind.CallDepth => MaximumCallDepth, 
				StfoLimitKind.ImportDepth => MaximumImportDepth, 
				StfoLimitKind.SourceKB => MaximumSourceKB, 
				StfoLimitKind.CollectionSize => MaximumCollectionSize, 
				StfoLimitKind.StringLength => MaximumStringLength, 
				_ => throw new ArgumentOutOfRangeException("kind"), 
			};
		}
	}
	internal abstract record StfoSyntaxNode(StfoToken Token);
	internal sealed record StfoProgramSyntax(IReadOnlyList<StfoStatementSyntax> Statements, IReadOnlyList<StfoDiagnostic> Diagnostics);
	internal abstract record StfoStatementSyntax : StfoSyntaxNode
	{
		protected StfoStatementSyntax(StfoToken Token)
			: base(Token)
		{
		}

		[CompilerGenerated]
		public new void Deconstruct(out StfoToken Token)
		{
			Token = base.Token;
		}
	}
	internal sealed record StfoImportStatementSyntax : StfoStatementSyntax
	{
		public string Path { get; init; }

		public StfoImportStatementSyntax(StfoToken Token, string Path)
		{
			this.Path = Path;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out string Path)
		{
			Token = base.Token;
			Path = this.Path;
		}
	}
	internal sealed record StfoAssignmentStatementSyntax : StfoStatementSyntax
	{
		public string Name { get; init; }

		public StfoExpressionSyntax Expression { get; init; }

		public StfoAssignmentStatementSyntax(StfoToken Token, string Name, StfoExpressionSyntax Expression)
		{
			this.Name = Name;
			this.Expression = Expression;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out string Name, out StfoExpressionSyntax Expression)
		{
			Token = base.Token;
			Name = this.Name;
			Expression = this.Expression;
		}
	}
	internal sealed record StfoPrintStatementSyntax : StfoStatementSyntax
	{
		public StfoExpressionSyntax Expression { get; init; }

		public StfoPrintStatementSyntax(StfoToken Token, StfoExpressionSyntax Expression)
		{
			this.Expression = Expression;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out StfoExpressionSyntax Expression)
		{
			Token = base.Token;
			Expression = this.Expression;
		}
	}
	internal sealed record StfoOutputStatementSyntax : StfoStatementSyntax
	{
		public StfoExpressionSyntax Expression { get; init; }

		public StfoOutputStatementSyntax(StfoToken Token, StfoExpressionSyntax Expression)
		{
			this.Expression = Expression;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out StfoExpressionSyntax Expression)
		{
			Token = base.Token;
			Expression = this.Expression;
		}
	}
	internal sealed record StfoExpressionStatementSyntax : StfoStatementSyntax
	{
		public StfoExpressionSyntax Expression { get; init; }

		public StfoExpressionStatementSyntax(StfoToken Token, StfoExpressionSyntax Expression)
		{
			this.Expression = Expression;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out StfoExpressionSyntax Expression)
		{
			Token = base.Token;
			Expression = this.Expression;
		}
	}
	internal sealed record StfoIfStatementSyntax : StfoStatementSyntax
	{
		public StfoExpressionSyntax Condition { get; init; }

		public IReadOnlyList<StfoStatementSyntax> ThenStatements { get; init; }

		public IReadOnlyList<StfoStatementSyntax> ElseStatements { get; init; }

		public StfoIfStatementSyntax(StfoToken Token, StfoExpressionSyntax Condition, IReadOnlyList<StfoStatementSyntax> ThenStatements, IReadOnlyList<StfoStatementSyntax> ElseStatements)
		{
			this.Condition = Condition;
			this.ThenStatements = ThenStatements;
			this.ElseStatements = ElseStatements;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out StfoExpressionSyntax Condition, out IReadOnlyList<StfoStatementSyntax> ThenStatements, out IReadOnlyList<StfoStatementSyntax> ElseStatements)
		{
			Token = base.Token;
			Condition = this.Condition;
			ThenStatements = this.ThenStatements;
			ElseStatements = this.ElseStatements;
		}
	}
	internal sealed record StfoFunctionStatementSyntax : StfoStatementSyntax
	{
		public string Name { get; init; }

		public IReadOnlyList<string> Parameters { get; init; }

		public IReadOnlyList<StfoStatementSyntax> Body { get; init; }

		public StfoFunctionStatementSyntax(StfoToken Token, string Name, IReadOnlyList<string> Parameters, IReadOnlyList<StfoStatementSyntax> Body)
		{
			this.Name = Name;
			this.Parameters = Parameters;
			this.Body = Body;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out string Name, out IReadOnlyList<string> Parameters, out IReadOnlyList<StfoStatementSyntax> Body)
		{
			Token = base.Token;
			Name = this.Name;
			Parameters = this.Parameters;
			Body = this.Body;
		}
	}
	internal sealed record StfoReturnStatementSyntax : StfoStatementSyntax
	{
		public StfoExpressionSyntax Expression { get; init; }

		public StfoReturnStatementSyntax(StfoToken Token, StfoExpressionSyntax Expression)
		{
			this.Expression = Expression;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out StfoExpressionSyntax Expression)
		{
			Token = base.Token;
			Expression = this.Expression;
		}
	}
	internal sealed record StfoForStatementSyntax : StfoStatementSyntax
	{
		public string Variable { get; init; }

		public StfoExpressionSyntax Collection { get; init; }

		public IReadOnlyList<StfoStatementSyntax> Body { get; init; }

		public StfoForStatementSyntax(StfoToken Token, string Variable, StfoExpressionSyntax Collection, IReadOnlyList<StfoStatementSyntax> Body)
		{
			this.Variable = Variable;
			this.Collection = Collection;
			this.Body = Body;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out string Variable, out StfoExpressionSyntax Collection, out IReadOnlyList<StfoStatementSyntax> Body)
		{
			Token = base.Token;
			Variable = this.Variable;
			Collection = this.Collection;
			Body = this.Body;
		}
	}
	internal sealed record StfoWhileStatementSyntax : StfoStatementSyntax
	{
		public StfoExpressionSyntax Condition { get; init; }

		public IReadOnlyList<StfoStatementSyntax> Body { get; init; }

		public StfoWhileStatementSyntax(StfoToken Token, StfoExpressionSyntax Condition, IReadOnlyList<StfoStatementSyntax> Body)
		{
			this.Condition = Condition;
			this.Body = Body;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out StfoExpressionSyntax Condition, out IReadOnlyList<StfoStatementSyntax> Body)
		{
			Token = base.Token;
			Condition = this.Condition;
			Body = this.Body;
		}
	}
	internal abstract record StfoExpressionSyntax : StfoSyntaxNode
	{
		protected StfoExpressionSyntax(StfoToken Token)
			: base(Token)
		{
		}

		[CompilerGenerated]
		public new void Deconstruct(out StfoToken Token)
		{
			Token = base.Token;
		}
	}
	internal sealed record StfoLiteralExpressionSyntax : StfoExpressionSyntax
	{
		public object? Value { get; init; }

		public StfoLiteralExpressionSyntax(StfoToken Token, object? Value)
		{
			this.Value = Value;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out object? Value)
		{
			Token = base.Token;
			Value = this.Value;
		}
	}
	internal sealed record StfoNameExpressionSyntax : StfoExpressionSyntax
	{
		public string Name { get; init; }

		public StfoNameExpressionSyntax(StfoToken Token, string Name)
		{
			this.Name = Name;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out string Name)
		{
			Token = base.Token;
			Name = this.Name;
		}
	}
	internal sealed record StfoUnaryExpressionSyntax : StfoExpressionSyntax
	{
		public StfoTokenKind Operator { get; init; }

		public StfoExpressionSyntax Operand { get; init; }

		public StfoUnaryExpressionSyntax(StfoToken Token, StfoTokenKind Operator, StfoExpressionSyntax Operand)
		{
			this.Operator = Operator;
			this.Operand = Operand;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out StfoTokenKind Operator, out StfoExpressionSyntax Operand)
		{
			Token = base.Token;
			Operator = this.Operator;
			Operand = this.Operand;
		}
	}
	internal sealed record StfoBinaryExpressionSyntax : StfoExpressionSyntax
	{
		public StfoExpressionSyntax Left { get; init; }

		public StfoTokenKind Operator { get; init; }

		public StfoExpressionSyntax Right { get; init; }

		public StfoBinaryExpressionSyntax(StfoToken Token, StfoExpressionSyntax Left, StfoTokenKind Operator, StfoExpressionSyntax Right)
		{
			this.Left = Left;
			this.Operator = Operator;
			this.Right = Right;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out StfoExpressionSyntax Left, out StfoTokenKind Operator, out StfoExpressionSyntax Right)
		{
			Token = base.Token;
			Left = this.Left;
			Operator = this.Operator;
			Right = this.Right;
		}
	}
	internal sealed record StfoCallExpressionSyntax : StfoExpressionSyntax
	{
		public string Name { get; init; }

		public IReadOnlyList<StfoExpressionSyntax> Arguments { get; init; }

		public StfoCallExpressionSyntax(StfoToken Token, string Name, IReadOnlyList<StfoExpressionSyntax> Arguments)
		{
			this.Name = Name;
			this.Arguments = Arguments;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out string Name, out IReadOnlyList<StfoExpressionSyntax> Arguments)
		{
			Token = base.Token;
			Name = this.Name;
			Arguments = this.Arguments;
		}
	}
	internal sealed record StfoListExpressionSyntax : StfoExpressionSyntax
	{
		public IReadOnlyList<StfoExpressionSyntax> Items { get; init; }

		public StfoListExpressionSyntax(StfoToken Token, IReadOnlyList<StfoExpressionSyntax> Items)
		{
			this.Items = Items;
			base..ctor(Token);
		}

		[CompilerGenerated]
		public void Deconstruct(out StfoToken Token, out IReadOnlyList<StfoExpressionSyntax> Items)
		{
			Token = base.Token;
			Items = this.Items;
		}
	}
	internal enum StfoTokenKind
	{
		EndOfFile,
		NewLine,
		Identifier,
		Number,
		String,
		At,
		LeftParenthesis,
		RightParenthesis,
		LeftBracket,
		RightBracket,
		Comma,
		Colon,
		Dot,
		Assign,
		Pipe,
		Plus,
		Minus,
		Star,
		Slash,
		EqualEqual,
		NotEqual,
		Less,
		LessOrEqual,
		Greater,
		GreaterOrEqual
	}
	internal sealed record StfoToken(StfoTokenKind Kind, string Text, int Line, int Column, int Offset);
	internal sealed record StfoLexResult(IReadOnlyList<StfoToken> Tokens, IReadOnlyList<StfoDiagnostic> Diagnostics);
}