Add named function parameters

This commit is contained in:
Stone_Red
2026-08-06 21:00:18 +02:00
parent e7bf9591ba
commit 6bc5d01a2e
10 changed files with 220 additions and 15 deletions
@@ -27,6 +27,7 @@ internal sealed class RuntimeInformation : IStatementContext
public Dictionary<string, string> GlobalVariables { get; set; } = [];
public Dictionary<string, int> Functions { get; } = [];
public Dictionary<string, List<string>> FunctionParameters { get; } = [];
public Dictionary<int, int> BlockBoundaries { get; } = [];
internal Action PreScanLinesAction { get; set; }
public Stack<FunctionScope> FunctionCallStack { get; } = new();
@@ -176,6 +177,7 @@ internal sealed class RuntimeInformation : IStatementContext
GlobalVariables.Clear();
Labels.Clear();
Functions.Clear();
FunctionParameters.Clear();
BlockBoundaries.Clear();
FunctionCallStack.Clear();
InParametersStack.Clear();
@@ -1,5 +1,8 @@
namespace YesNt.Interpreter.Runtime;
using System;
using System.Collections.Generic;
/// <summary>
/// Base class for all classes that host statement handler methods.
/// Subclasses declare methods decorated with <see cref="Attributes.StatementAttribute"/> or
@@ -21,4 +24,78 @@ internal abstract class StatementRuntimeInformation
{
return value.Trim().TrimEnd(':').Trim();
}
/// <summary>
/// Parses a function declaration (the text after the <c>func</c> keyword), extracting the
/// function name and its optional comma-separated named parameters.
/// </summary>
/// <returns>
/// <see langword="true"/> if the declaration is well formed; otherwise <see langword="false"/>.
/// </returns>
internal static bool TryParseFunctionSignature(string declaration, out string name, out List<string> parameters)
{
name = string.Empty;
parameters = [];
string trimmed = declaration.Trim();
int colonIndex = trimmed.IndexOf(':');
if (colonIndex < 0)
{
return false;
}
name = NormalizeBlockName(trimmed[..colonIndex]);
if (string.IsNullOrWhiteSpace(name))
{
return false;
}
string parameterSection = trimmed[(colonIndex + 1)..].Trim();
if (string.IsNullOrWhiteSpace(parameterSection))
{
return true;
}
foreach (string rawParameter in parameterSection.Split(','))
{
string parameter = rawParameter.Trim();
if (string.IsNullOrWhiteSpace(parameter) || parameter.Contains(' '))
{
return false;
}
parameters.Add(parameter);
}
return true;
}
/// <summary>
/// Binds a function's named parameters to local variables by popping values from the
/// current call scope's argument stack, mirroring <c>var p = %in</c> for each parameter.
/// </summary>
protected void BindNamedParameters(string functionKey)
{
if (!RuntimeInfo.FunctionParameters.TryGetValue(functionKey, out List<string> parameters) || parameters.Count == 0)
{
return;
}
if (RuntimeInfo.FunctionCallStack.Count == 0)
{
return;
}
FunctionScope scope = RuntimeInfo.FunctionCallStack.Peek();
foreach (string parameter in parameters)
{
if (scope.Arguments.Count == 0)
{
RuntimeInfo.Exit(ExitMessages.NoInArgumentInStack, true);
return;
}
scope.Variables[parameter] = scope.Arguments.Pop();
}
}
}
@@ -597,6 +597,7 @@ public class YesNtInterpreter
internal void PreScanLines()
{
runtimeInfo.BlockBoundaries.Clear();
runtimeInfo.FunctionParameters.Clear();
lineMatchingHandlers = new List<List<StatementHandler>>(runtimeInfo.Lines.Count);
// Dictionary to track open blocks by their expected end statement name
@@ -615,6 +616,12 @@ public class YesNtInterpreter
{
matchingHandlers.Add(handler);
if (handler.Attribute.Name == "func"
&& StatementRuntimeInformation.TryParseFunctionSignature(content["func ".Length..], out string functionName, out List<string> functionParameters))
{
runtimeInfo.FunctionParameters[functionName] = functionParameters;
}
// Track block starts (skip intermediates — they are handled separately below)
string blockPair = handler.Attribute.BlockPair;
if (!string.IsNullOrEmpty(blockPair) && !handler.Attribute.IsBlockIntermediate)
@@ -248,6 +248,7 @@ internal class CodeFlowStatements : StatementRuntimeInformation
{
RuntimeInfo.FunctionCallStack.Push(new FunctionScope(RuntimeInfo.LineNumber, new Stack<string>(RuntimeInfo.InParametersStack)));
RuntimeInfo.InParametersStack.Clear();
BindNamedParameters(key);
if (RuntimeInfo.Functions.TryGetValue(key, out int value))
{
@@ -19,17 +19,9 @@ internal class FunctionStatements : StatementRuntimeInformation
return;
}
string functionDeclaration = args.Trim();
if (!functionDeclaration.EndsWith(':'))
if (!TryParseFunctionSignature(args, out string key, out _))
{
RuntimeInfo.Exit(ExitMessages.InvalidSyntaxColonRequired, true);
return;
}
string key = NormalizeBlockName(functionDeclaration);
if (string.IsNullOrWhiteSpace(key))
{
RuntimeInfo.Exit(ExitMessages.InvalidSyntax, true);
RuntimeInfo.Exit(args.Trim().Contains(':') ? ExitMessages.InvalidSyntax : ExitMessages.InvalidSyntaxColonRequired, true);
return;
}
@@ -83,6 +75,7 @@ internal class FunctionStatements : StatementRuntimeInformation
RuntimeInfo.FunctionCallStack.Push(new FunctionScope(RuntimeInfo.LineNumber, new Stack<string>(RuntimeInfo.InParametersStack)));
RuntimeInfo.InParametersStack.Clear();
BindNamedParameters(key);
RuntimeInfo.CurrentLine = string.Empty;
if (RuntimeInfo.Functions.TryGetValue(key, out int value))