From 39066900773e7857faf2d02e7ed51b71d219d97d Mon Sep 17 00:00:00 2001 From: godosa Date: Tue, 6 Oct 2026 23:39:36 +0200 Subject: godosa-engine: initial public history --- src/Godosa.Core/Runs/Scripts/StepLexer.cs | 104 ++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/Godosa.Core/Runs/Scripts/StepLexer.cs (limited to 'src/Godosa.Core/Runs/Scripts/StepLexer.cs') diff --git a/src/Godosa.Core/Runs/Scripts/StepLexer.cs b/src/Godosa.Core/Runs/Scripts/StepLexer.cs new file mode 100644 index 0000000..4c54544 --- /dev/null +++ b/src/Godosa.Core/Runs/Scripts/StepLexer.cs @@ -0,0 +1,104 @@ +// Provenance: own design — shared run scripts +using System.Globalization; +using System.Text; + +namespace Godosa.Core.Runs.Scripts; + +public sealed record Token(string Text, bool Quoted); + +/// Line lexing for .steps: # comments outside quotes; tokens split on spaces, "…" (escapes +/// \", \\) and (…) groups stay one token. +public static class StepLexer +{ + public static string StripComment(string line) + { + var quoted = false; + for (var i = 0; i < line.Length; i++) + { + var c = line[i]; + if (quoted && c == '\\' && i + 1 < line.Length) + { + i++; + continue; + } + if (c == '"') + quoted = !quoted; + else if (c == '#' && !quoted) + return line[..i]; + } + return line; + } + + public static List Tokens(string text) + { + var tokens = new List(); + var i = 0; + while (i < text.Length) + { + if (text[i] == ' ') + { + i++; + continue; + } + if (text[i] == '"') + { + var sb = new StringBuilder(); + i++; + while (true) + { + if (i >= text.Length) + throw new FormatException("unterminated quote"); + var c = text[i]; + if (c == '\\' && i + 1 < text.Length) + { + sb.Append(text[i + 1]); + i += 2; + continue; + } + if (c == '"') + { + i++; + break; + } + sb.Append(c); + i++; + } + tokens.Add(new Token(sb.ToString(), true)); + continue; + } + var start = i; + var depth = 0; + while (i < text.Length && (depth > 0 || text[i] != ' ')) + { + if (text[i] == '(') + depth++; + else if (text[i] == ')' && depth > 0) + depth--; + i++; + } + tokens.Add(new Token(text[start..i], false)); + } + return tokens; + } +} + +/// Timeouts and waits in frames (1 frame = 1/60 s): 120 = 120 frames, 2s = 120, 0.5s = 30; +/// negative, non-whole frame counts and other units → null. +public static class Durations +{ + public const int FramesPerSecond = 60; + + public static int? Frames(string text) + { + if (text.EndsWith('s')) + { + var number = text[..^1]; + if (number.Length == 0 || !number.All(c => char.IsAsciiDigit(c) || c == '.') + || !decimal.TryParse(number, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var seconds)) + return null; + var frames = seconds * FramesPerSecond; + return frames == decimal.Truncate(frames) ? (int)frames : null; + } + return text.Length > 0 && text.All(char.IsAsciiDigit) && int.TryParse(text, CultureInfo.InvariantCulture, out var n) ? n : null; + } +} -- cgit