1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
// Provenance: own design — shared run scripts
namespace Godosa.Core.Runs.Scripts;
public enum Op { Verb, Wait, Expect, Set, Checkpoint, LabelBegin, LabelEnd, ScopeBegin, ScopeEnd, TryBegin, TryEnd, SetupBegin, SetupEnd }
/// <summary>One flattened instruction. <see cref="Chain"/> = include / call sites, outermost first.</summary>
public sealed record Instr(Op Op, SourceRef At, string Text, IReadOnlyList<SourceRef> Chain, int Format)
{
public string A { get; init; } = "";
public string B { get; init; } = "";
public int Timeout { get; init; }
public IReadOnlyList<KeyValuePair<string, string>> Bindings { get; init; } = [];
public bool LocalVars { get; init; }
public int Jump { get; init; } = -1;
}
public sealed record ScriptProgram(string Entry, RunMode Mode, ulong? Seed, IReadOnlyDictionary<string, string> Vars,
IReadOnlySet<string> Locked, IReadOnlyList<Instr> Code, IReadOnlyList<ScriptError> Errors);
public interface IScriptSource
{
string? Read(string path);
}
public sealed class MemoryScriptSource(IReadOnlyDictionary<string, string> files) : IScriptSource
{
public string? Read(string path) => files.GetValueOrDefault(path);
}
/// <summary>Files under <paramref name="root"/>; paths are '/'-separated and relative to it.</summary>
public sealed class FileScriptSource(string root) : IScriptSource
{
public string? Read(string path)
{
var full = Path.GetFullPath(Path.Combine(root, path));
return File.Exists(full) ? File.ReadAllText(full) : null;
}
}
public static class ScriptPaths
{
public static string Resolve(string fromFile, string relative)
{
var dir = fromFile.Contains('/') ? fromFile[..fromFile.LastIndexOf('/')] : "";
var parts = new List<string>(dir.Length == 0 ? [] : dir.Split('/'));
foreach (var part in relative.Replace('\\', '/').Split('/'))
{
if (part is "" or ".")
continue;
if (part == ".." && parts.Count > 0)
parts.RemoveAt(parts.Count - 1);
else if (part != "..")
parts.Add(part);
}
return string.Join('/', parts);
}
}
|