-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathWorkspace.cs
More file actions
161 lines (128 loc) · 4.54 KB
/
Copy pathWorkspace.cs
File metadata and controls
161 lines (128 loc) · 4.54 KB
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using BlocklyNet.Core.Blocks;
using BlocklyNet.Core.Blocks.Procedures;
using BlocklyNet.Core.Blocks.Text;
using BlocklyNet.Extensions;
using BlocklyNet.Scripting.Engine;
namespace BlocklyNet.Core.Model;
/// <summary>
/// A blocky workspace.
/// </summary>
public class Workspace : IFragment
{
/// <summary>
/// All blocks in the workspace.
/// </summary>
public readonly IList<Block> Blocks = [];
/// <summary>
/// All parsed blocks in the workspace - any nesting.
/// </summary>
public readonly IList<Block> ParsedBlocks = [];
/// <summary>
/// Mapping of variables to its types.
/// </summary>
public readonly Dictionary<string, string> VariableTypes = [];
/// <summary>
/// Execute all blocks in the workspace.
/// </summary>
/// <param name="context">Execution context.</param>
/// <returns>Result of the final block in the workspace.</returns>
public async Task<object?> EvaluateAsync(Context context)
{
/* No result at all. */
object? returnValue = null;
/* Find all functions. */
var functions = new List<Block>();
foreach (var block in Blocks.OfType<ProceduresDef>())
{
/* Create the function itself and remember it. */
context.Cancellation.ThrowIfCancellationRequested();
await block.PrepareAsync(context);
functions.Add(block);
}
/* Process any block which is not a function. */
foreach (var block in Blocks)
if (!functions.Contains(block))
for (var exec = block; exec != null; exec = exec.Next)
if (exec.Enabled)
{
/* Remember the result and report the last result afterwards. */
context.Cancellation.ThrowIfCancellationRequested();
returnValue = await exec.EnterBlockAsync(context, true);
/* Did this path. */
break;
}
return returnValue;
}
private void InspectBlockChain(Block? block, GroupInfo scope, Context context, HashSet<string> activeFunctions)
{
for (; block != null; block = block.Next)
{
var call = block is ProceduresCallNoReturn procedure ? procedure
: block is ProceduresCallReturn function ? function
: null;
if (call != null)
{
// Some procedure call.
var name = call.Mutations.GetValue("name");
if (!context.Functions.TryGetValue(name, out var def)) continue;
if (!activeFunctions.Add(name)) continue;
foreach (var value in block.Values)
InspectBlockChain(value.Block, scope, context, activeFunctions);
InspectBlockChain(def.Block, scope, context, activeFunctions);
activeFunctions.Remove(name);
}
else if (block is RunScript script)
{
// Some script call - or at least the preparation for it.
var name = script.Values.TryGet("NAME");
if (name?.Block is TextBlock text)
scope.Scripts.Add(text.Fields["TEXT"]);
}
{
// Regular block - maybe a nested group execution.
var info = block is ExecutionGroup group
? new GroupInfo { Id = block.Id, Name = group.Fields["NAME"] }
: scope;
if (info != scope)
scope.Children.Add(info);
// Inspect all values.
foreach (var value in block.Values)
InspectBlockChain(value.Block, info, context, activeFunctions);
// Inspect all statements.
foreach (var statement in block.Statements)
InspectBlockChain(statement.Block, info, context, activeFunctions);
}
}
}
private static void RemoveScriptNameDuplicates(IEnumerable<GroupInfo> groups)
{
foreach (var group in groups)
{
// Make sure that each name is referenced only once.
group.Scripts = [.. group.Scripts.ToHashSet()];
// Recurse to full tree.
RemoveScriptNameDuplicates(group.Children);
}
}
/// <summary>
/// Get the hierarchy of groups based on a static analysis of
/// the script.
/// </summary>
/// <returns>The group information tree.</returns>
public async Task<List<GroupInfo>> GetGroupTreeAsync()
{
/* Resulting list. */
var scope = new GroupInfo();
/* Use a dummy site. */
var context = new Context(null!, VariableTypes);
/* Find all functions and generate the block list. */
foreach (var block in Blocks.OfType<ProceduresDef>())
await block.PrepareAsync(context);
/* Inspect all blocks. */
foreach (var block in Blocks)
if (block is not ProceduresDef)
InspectBlockChain(block, scope, context, []);
RemoveScriptNameDuplicates(scope.Children);
return scope.Children;
}
}