Skip to content

Commit 937daee

Browse files
committed
Abstract the Hypervisor
This allows custom implementations without 1:1 copying the library source for the entire class. At a bare minimum, custom Hypervisor implementations need to fully implement every single instruction. You can still implement StarscriptHypervisor directly if you don't want to do that.
1 parent ec8e7c9 commit 937daee

19 files changed

+322
-194
lines changed

src/Lib/Internal/ExecutableScript.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
namespace Starscript.Internal;
1+
using Starscript.Abstraction;
2+
3+
namespace Starscript.Internal;
24

35
public abstract class ExecutableScript : IDisposable
46
{
@@ -16,12 +18,15 @@ public abstract class ExecutableScript : IDisposable
1618

1719
public int GetMasked(int idx) => GetByteAt(idx) & 0xFF;
1820

19-
public StringSegment Execute(StarscriptHypervisor hypervisor)
21+
public StringSegment Execute<THypervisor>(THypervisor hypervisor)
22+
where THypervisor : AbstractHypervisor<THypervisor>
2023
=> hypervisor.Run(this);
2124

22-
public StringSegment Execute(StarscriptHypervisor hypervisor, ValueMap locals)
25+
public StringSegment Execute<THypervisor>(THypervisor hypervisor, ValueMap locals)
26+
where THypervisor : AbstractHypervisor<THypervisor>
2327
=> hypervisor.Run(this, locals);
2428

25-
public StringSegment Execute(StarscriptHypervisor hypervisor, IStarscriptObject obj)
29+
public StringSegment Execute<THypervisor>(THypervisor hypervisor, IStarscriptObject obj)
30+
where THypervisor : AbstractHypervisor<THypervisor>
2631
=> hypervisor.Run(this, obj);
2732
}

src/Lib/Public/Hypervisor/Starscript.Completions.cs renamed to src/Lib/Public/Hypervisor/Abstraction/AbstractHypervisor.Completions.cs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
using Starscript.Internal;
2-
// ReSharper disable MemberCanBePrivate.Global
3-
// ReSharper disable UnusedMember.Global
42

5-
namespace Starscript;
3+
namespace Starscript.Abstraction;
64

7-
public partial class StarscriptHypervisor
5+
public abstract partial class AbstractHypervisor<TSelf>
86
{
97
/// <summary>
108
/// Calls the provided callback for every completion that can be resolved from global variables, and returns the parsed <paramref name="source"/>.
@@ -13,7 +11,7 @@ public partial class StarscriptHypervisor
1311
/// <param name="position">The position of the caret.</param>
1412
/// <param name="callback">What to do with each completion suggestion.</param>
1513
/// <param name="cancellationToken">A cancellation token you can use to short-circuit return from completion logic on a best-effort basis.</param>
16-
public Parser.Result ParseAndGetCompletions(string source, int position, CompletionCallback callback,
14+
public virtual Parser.Result ParseAndGetCompletions(string source, int position, CompletionCallback callback,
1715
CancellationToken cancellationToken = default)
1816
{
1917
var parserResult = Parser.Parse(source);
@@ -31,7 +29,7 @@ public Parser.Result ParseAndGetCompletions(string source, int position, Complet
3129

3230
return parserResult;
3331
}
34-
32+
3533
/// <summary>
3634
/// Calls the provided callback for every completion that can be resolved from global variables.
3735
/// </summary>
@@ -42,8 +40,8 @@ public Parser.Result ParseAndGetCompletions(string source, int position, Complet
4240
public void GetCompletions(string source, int position, CompletionCallback callback,
4341
CancellationToken cancellationToken = default)
4442
=> _ = ParseAndGetCompletions(source, position, callback, cancellationToken);
45-
46-
private void CompletionsExpr(string source, int pos, Expr expr, CompletionCallback callback,
43+
44+
protected virtual void CompletionsExpr(string source, int pos, Expr expr, CompletionCallback callback,
4745
CancellationToken cancellationToken)
4846
{
4947
if (cancellationToken.IsCancellationRequested)
@@ -124,7 +122,7 @@ private void CompletionsExpr(string source, int pos, Expr expr, CompletionCallba
124122
}
125123
}
126124

127-
private Value? Resolve(Expr expr)
125+
protected Value? Resolve(Expr expr)
128126
{
129127
if (expr is Expr.Variable variableExpr)
130128
{
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
using System.Diagnostics.CodeAnalysis;
22

3-
namespace Starscript;
3+
namespace Starscript.Abstraction;
44

5-
public partial class StarscriptHypervisor
5+
public partial class AbstractHypervisor<TSelf>
66
{
77
/// <summary>
88
/// Sets a variable supplier for the provided name.
99
/// </summary>
10-
public StarscriptHypervisor Set(string name, Func<Value> supplier)
10+
public TSelf Set(string name, Func<Value> supplier)
1111
{
1212
Globals.Set(name, supplier);
1313
return this;
@@ -16,7 +16,7 @@ public StarscriptHypervisor Set(string name, Func<Value> supplier)
1616
/// <summary>
1717
/// Sets a variable supplier that always returns the same value for the provided name.
1818
/// </summary>
19-
public StarscriptHypervisor Set(string name, Value value)
19+
public TSelf Set(string name, Value value)
2020
{
2121
Globals.Set(name, value);
2222
return this;
@@ -25,7 +25,7 @@ public StarscriptHypervisor Set(string name, Value value)
2525
/// <summary>
2626
/// Sets a variable supplier that always returns the same <see cref="IStarscriptObject"/> for the provided name.
2727
/// </summary>
28-
public StarscriptHypervisor Set(string name, IStarscriptObject value)
28+
public TSelf Set(string name, IStarscriptObject value)
2929
{
3030
Globals.Set(name, value);
3131
return this;
@@ -34,25 +34,25 @@ public StarscriptHypervisor Set(string name, IStarscriptObject value)
3434
/// <summary>
3535
/// Sets a function variable supplier that always returns the same value for the provided name.
3636
/// </summary>
37-
public StarscriptHypervisor Set(string name, StarscriptFunction function)
37+
public TSelf Set(string name, StarscriptFunction function)
3838
{
3939
Globals.Set(name, function);
4040
return this;
4141
}
4242

43-
public StarscriptHypervisor Set(string name, Constraint constraint, ContextualStarscriptFunction contextualFunction)
43+
public TSelf Set(string name, Constraint constraint, ContextualStarscriptFunction contextualFunction)
4444
{
4545
Globals.Set(name, constraint, contextualFunction);
4646
return this;
4747
}
4848

49-
public StarscriptHypervisor Set(string name, ContextualStarscriptFunction contextualFunction)
49+
public TSelf Set(string name, ContextualStarscriptFunction contextualFunction)
5050
{
5151
Globals.Set(name, contextualFunction);
5252
return this;
5353
}
5454

55-
public StarscriptHypervisor NewSubMap(string name, Action<ValueMap> init)
55+
public TSelf NewSubMap(string name, Action<ValueMap> init)
5656
{
5757
var map = new ValueMap();
5858
init(map);
@@ -65,7 +65,7 @@ public StarscriptHypervisor NewSubMap(string name, Action<ValueMap> init)
6565
/// </summary>
6666
/// <param name="map">A reference to the new globals map.</param>
6767
/// <remarks>StandardLibrary variables &amp; functions are provided via the globals map, so use one of the helpers in <see cref="StandardLibrary"/> to add them back if you want.</remarks>
68-
public StarscriptHypervisor ReplaceGlobals(ValueMap map)
68+
public TSelf ReplaceGlobals(ValueMap map)
6969
{
7070
Globals = map;
7171
return this;
@@ -74,30 +74,20 @@ public StarscriptHypervisor ReplaceGlobals(ValueMap map)
7474
/// <summary>
7575
/// Sets an object variable supplier that always returns the same value for the provided name.
7676
/// </summary>
77-
public StarscriptHypervisor Set(string name, object obj)
77+
public TSelf Set(string name, object obj)
7878
{
7979
Globals.Set(name, TopLevelFunctions.Object(obj));
8080
return this;
8181
}
8282

83-
/// <summary>
84-
/// Removes all values from the globals.
85-
/// </summary>
86-
public void Clear() => Globals.Clear();
87-
88-
internal Func<Value>? ResolveVariable(string name) => Locals?.GetRaw(name) ?? Globals.GetRaw(name);
89-
90-
internal Value? GetVariable(string name) => ResolveVariable(name)?.Invoke();
91-
9283
/// <summary>
9384
/// Removes a single value with the specified name from the globals and returns the removed value.
9485
/// </summary>
9586
public bool Remove(string name, [MaybeNullWhen(false)] out Func<Value> removedValue)
9687
=> Globals.Remove(name, out removedValue);
9788

9889
/// <summary>
99-
/// Returns a new <see cref="StarscriptHypervisor"/> with the globals inherited from this one.
100-
/// Useful for maintaining multiple <see cref="StarscriptHypervisor"/>s for varied use-cases, inheriting from a single globals map with minor differences.
90+
/// Removes all values from the globals.
10191
/// </summary>
102-
public StarscriptHypervisor CopyGlobalsToNew() => CreateFromParent(this);
92+
public void Clear() => Globals.Clear();
10393
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System.Text;
2+
using Starscript.Internal;
3+
4+
namespace Starscript.Abstraction;
5+
6+
public partial class AbstractHypervisor<TSelf>
7+
{
8+
protected abstract void Jump(ref ExecutableScript script, ref int insnPtr);
9+
protected abstract void JumpIfTrue(ref ExecutableScript script, ref int insnPtr);
10+
protected abstract void JumpIfFalse(ref ExecutableScript script, ref int insnPtr);
11+
protected abstract void Not();
12+
protected abstract void Equals();
13+
protected abstract void NotEquals();
14+
protected abstract void Greater();
15+
protected abstract void GreaterEqual();
16+
protected abstract void Less();
17+
protected abstract void LessEqual();
18+
19+
protected abstract void Add();
20+
protected abstract void Negate();
21+
protected abstract void Subtract();
22+
protected abstract void Multiply();
23+
protected abstract void Divide();
24+
protected abstract void Modulo();
25+
protected abstract void Power();
26+
protected abstract void RightShift();
27+
protected abstract void LeftShift();
28+
29+
protected abstract void Section(
30+
ref StringBuilder sb,
31+
ref ExecutableScript script,
32+
ref StringSegment firstSegment,
33+
ref StringSegment segment,
34+
ref int index,
35+
ref int insnPtr);
36+
37+
protected abstract void Append(ref StringBuilder sb);
38+
protected abstract void ConstantAppend(ref StringBuilder sb, ref ExecutableScript script, ref int insnPtr);
39+
protected abstract void VariableAppend(ref StringBuilder sb, ref ExecutableScript script, ref int insnPtr);
40+
protected abstract void GetAppend(ref StringBuilder sb, ref ExecutableScript script, ref int insnPtr);
41+
protected abstract void CallAppend(ref StringBuilder sb, ref ExecutableScript script, ref int insnPtr);
42+
protected abstract void VariableGetAppend(ref StringBuilder sb, ref ExecutableScript script, ref int insnPtr);
43+
protected abstract void AddConstant(ref ExecutableScript script, ref int insnPtr);
44+
protected abstract void Constant(ref ExecutableScript script, ref int insnPtr);
45+
protected abstract void Variable(ref ExecutableScript script, ref int insnPtr);
46+
protected abstract void Get(ref ExecutableScript script, ref int insnPtr);
47+
protected abstract void VariableGet(ref ExecutableScript script, ref int insnPtr);
48+
protected abstract void Call(ref ExecutableScript script, ref int insnPtr);
49+
50+
protected abstract StringSegment EndExecution(ref StringBuilder sb, ref StringSegment firstSegment,
51+
ref StringSegment segment, int index);
52+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace Starscript.Abstraction;
2+
3+
public abstract partial class AbstractHypervisor<TSelf>
4+
{
5+
public virtual TSelf ClearLocals()
6+
{
7+
if (!PersistentLocals)
8+
Locals = null;
9+
10+
return this;
11+
}
12+
13+
public TSelf ReplaceLocals(ValueMap locals)
14+
{
15+
Locals = locals;
16+
return this;
17+
}
18+
19+
public TSelf ReplaceLocals(IStarscriptObject locals)
20+
=> ReplaceLocals(locals.ToStarscript());
21+
}

src/Lib/Public/Hypervisor/Starscript.HV.cs renamed to src/Lib/Public/Hypervisor/Abstraction/AbstractHypervisor.Runner.cs

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22
using Starscript.Internal;
33
using Starscript.Util;
44

5-
namespace Starscript;
5+
namespace Starscript.Abstraction;
66

7-
public partial class StarscriptHypervisor
7+
public partial class AbstractHypervisor<TSelf>
88
{
9-
internal StringSegment RunImpl(ExecutableScript script, StringBuilder sb)
9+
protected virtual StringSegment RunInternal(ExecutableScript script, StringBuilder sb)
1010
{
1111
if (script.IsDisposed)
1212
throw new ObjectDisposedException(script.GetType().FullName, "Cannot execute a disposed Script.");
1313

14-
_stack.Clear();
14+
ClearStack();
1515

1616
sb.Length = 0;
1717

@@ -80,28 +80,15 @@ internal StringSegment RunImpl(ExecutableScript script, StringBuilder sb)
8080

8181
case Instruction.Section:
8282
{
83-
if (firstSegment is null)
84-
{
85-
firstSegment = new StringSegment(index, sb.ToString());
86-
segment = firstSegment;
87-
}
88-
else
89-
{
90-
segment!.Next = new StringSegment(index, sb.ToString());
91-
segment = segment.Next;
92-
}
93-
94-
sb.Length = 0;
95-
index = script[instructionPointer++];
83+
Section(ref sb, ref script,
84+
ref firstSegment.NullableRef(),
85+
ref segment.NullableRef(),
86+
ref index, ref instructionPointer);
9687
break;
9788
}
9889

9990
case Instruction.End:
100-
#if DEBUG
101-
DebugLog("End of script code reached. Breaking execution.");
102-
#endif
103-
104-
goto EndExecution;
91+
return EndExecution(ref sb, ref firstSegment.NullableRef(), ref segment.NullableRef(), index);
10592
default:
10693
#if DEBUG
10794
throw new InvalidOperationException(
@@ -112,11 +99,5 @@ internal StringSegment RunImpl(ExecutableScript script, StringBuilder sb)
11299
#endif
113100
}
114101
}
115-
116-
EndExecution:
117-
118-
return EndExecution(ref sb, ref firstSegment.NullableRef(), ref segment.NullableRef(), index);
119102
}
120-
121-
private static void AppendValue(StringBuilder sb, Value? value) => sb.Append(value ?? Value.Null);
122103
}

src/Lib/Public/Hypervisor/Starscript.StackManipulation.cs renamed to src/Lib/Public/Hypervisor/Abstraction/AbstractHypervisor.Stack.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
using Starscript.Util;
22

3-
namespace Starscript;
3+
namespace Starscript.Abstraction;
44

5-
public partial class StarscriptHypervisor
5+
public partial class AbstractHypervisor<TSelf>
66
{
77
private readonly TransparentStack<Value> _stack = new();
88

@@ -16,7 +16,9 @@ public partial class StarscriptHypervisor
1616

1717
return (left, right);
1818
}
19-
19+
20+
protected void ClearStack() => _stack.Clear();
21+
2022
public Value Peek() => _stack.Peek();
2123
public Value Peek(int offset) => _stack.Peek(offset);
2224

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
using System.Runtime.CompilerServices;
3+
using System.Text;
4+
using Starscript.Internal;
5+
using Starscript.Util;
6+
7+
namespace Starscript.Abstraction;
8+
9+
public partial class AbstractHypervisor<TSelf>
10+
{
11+
public StringSegment Run(ExecutableScript script) => RunInternal(script, new StringBuilder());
12+
13+
/// <summary>
14+
/// Calls <see cref="Run(ExecutableScript)"/> after calling <see cref="ReplaceLocals(Starscript.ValueMap)"/>.
15+
/// </summary>
16+
/// <remarks>The internal locals map has been reset by the time this method returns, if persistent locals is disabled.</remarks>
17+
public StringSegment Run(ExecutableScript script, ValueMap locals)
18+
=> ReplaceLocals(locals).Run(script);
19+
20+
/// <summary>
21+
/// Calls <see cref="Run(ExecutableScript)"/> after calling <see cref="ReplaceLocals(Starscript.IStarscriptObject)"/>.
22+
/// </summary>
23+
/// <remarks>The internal locals map has been reset by the time this method returns, if persistent locals is disabled.</remarks>
24+
public StringSegment Run(ExecutableScript script, IStarscriptObject locals)
25+
=> ReplaceLocals(locals).Run(script);
26+
27+
public static implicit operator TSelf(AbstractHypervisor<TSelf> hv) => (TSelf)hv;
28+
29+
public static StarscriptException Error([StringSyntax("CompositeFormat")] string format, params object?[] args)
30+
=> new(args.Length == 0 ? format : string.Format(format, args));
31+
32+
#if DEBUG
33+
protected void DebugLog(string message,
34+
[CallerFilePath] string sourceLocation = default!,
35+
[CallerLineNumber] int lineNumber = default,
36+
[CallerMemberName] string callerName = default!)
37+
{
38+
if (DebugLogger.HypervisorOutput)
39+
// ReSharper disable ExplicitCallerInfoArgument
40+
DebugLogger.Print(DebugLogSource.Hypervisor, message, InvocationInfo.Here(sourceLocation, lineNumber, callerName));
41+
}
42+
#endif
43+
}

0 commit comments

Comments
 (0)