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);
}