aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/src/Godosa.Core/Runs/Scripts/ScriptProgram.cs
blob: 502e24c3ee7d8f3b9538b51e296a62f02b1b01e6 (plain)
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);
    }
}