Skip to content

Commit

Permalink
Group name must be a field for static analysis. (ZeraGmbH#52)
Browse files Browse the repository at this point in the history
* Update depnedencies.

* Group name must be a field for static analysis.
  • Loading branch information
JMS-1 authored Oct 9, 2024
1 parent 88f3f0a commit d0de21c
Show file tree
Hide file tree
Showing 8 changed files with 262 additions and 39 deletions.
4 changes: 2 additions & 2 deletions Library/BlocklyNet.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
Expand All @@ -12,7 +12,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.11.20" PrivateAssets="all" />
</ItemGroup>
Expand Down
42 changes: 42 additions & 0 deletions Library/Core/Model/Workspace.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using BlocklyNet.Core.Blocks.Text;
using BlocklyNet.Extensions;
using BlocklyNet.Scripting.Engine;

namespace BlocklyNet.Core.Model;

Expand Down Expand Up @@ -47,5 +49,45 @@ public class Workspace : IFragment

return returnValue;
}

private void InspectBlockChain(Block? block)
{
for (; block != null; block = block.Next)
{
System.Diagnostics.Debug.WriteLine(block.Type);

if (block is ExecutionGroup group)
{
System.Diagnostics.Debug.WriteLine(group.Fields["NAME"]);

InspectBlockChain(group.Values.Get("RESULT")?.Block);
}

foreach (var statement in block.Statements)
InspectBlockChain(statement.Block);
}
}

/// <summary>
/// Get the hierarchy of groups based on a static analysis of
/// the script.
/// </summary>
/// <returns>The group information tree.</returns>
public async Task<int> GetGroupTreeAsync()
{
/* Use a dummy site. */
var context = new Context((IScriptSite)null!);

/* Find all functions and generate the block list. */
foreach (var block in Blocks.OfType<ProceduresDef>())
await block.EvaluateAsync(context);

/* Inspect all blocks. */
foreach (var block in Blocks)
if (block is not ProceduresDef)
InspectBlockChain(block);

return 0;
}
}

5 changes: 5 additions & 0 deletions Library/Core/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ private class ParsedScript(Workspace workspace) : IParsedScript
{
private readonly Workspace _workspace = workspace;

/// <inheritdoc/>
public Task<object?> RunAsync(IScriptSite engine) => _workspace.EvaluateAsync(new Context(engine));

/// <inheritdoc/>
public async Task<object?> EvaluateAsync(Dictionary<string, object?> presets, IScriptSite site)
{
var ctx = new Context(site);
Expand All @@ -54,6 +56,9 @@ private class ParsedScript(Workspace workspace) : IParsedScript
/* No result at all. */
return null;
}

/// <inheritdoc/>
public Task<int> GetGroupTreeAsync() => _workspace.GetGroupTreeAsync();
}

public IParsedScript Parse(string scriptAsText)
Expand Down
26 changes: 7 additions & 19 deletions Library/Extensions/ExecutionGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@ namespace BlocklyNet.Extensions;
{
""type"": ""input_dummy""
},
{
""type"": ""field_label_serializable"",
{
""type"": ""field_input"",
""name"": ""NAME"",
""text"": ""Name of the group""
""text"": ""Name of the Group""
},
{
""type"": ""input_value"",
""name"": ""NAME"",
""check"": ""String""
""type"": ""input_dummy"",
""name"": ""NAME""
},
{
""type"": ""field_label_serializable"",
Expand All @@ -43,26 +42,15 @@ namespace BlocklyNet.Extensions;
""tooltip"": ""Execute something as a group."",
""helpUrl"": """"
}",
@"{
""inputs"": {
""NAME"": {
""shadow"": {
""type"": ""text"",
""fields"": {
""TEXT"": """"
}
}
}
}
}"
""
)]
public class ExecutionGroup : Block
{
/// <inheritdoc/>
public override async Task<object?> EvaluateAsync(Context context)
{
/* Register the group. */
var name = await Values.EvaluateAsync<string?>("NAME", context, false);
var name = Fields["NAME"];

if (context.Engine.BeginGroup(Id, name))
{
Expand Down
6 changes: 6 additions & 0 deletions Library/Scripting/Parsing/IParsedScript.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using BlocklyNet.Core.Model;
using BlocklyNet.Scripting.Engine;

namespace BlocklyNet.Scripting.Parsing;
Expand All @@ -21,4 +22,9 @@ public interface IParsedScript
/// <param name="engine"></param>
/// <returns></returns>
Task<object?> RunAsync(IScriptSite engine);

/// <summary>
///
/// </summary>
Task<int> GetGroupTreeAsync();
}
6 changes: 4 additions & 2 deletions Tests/CoreEx/GroupExecutionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ public async Task Can_Run_Simple_Groups_Async()
var block = new ExecutionGroup
{
Id = "A",
Fields = { new() { Name = "NAME", Value = "A1" } },
Values = { new() { Name = "RESULT", Block = new AnyValueBlock(new GroupResult { Result = "1", Type = GroupResultType.Succeeded }) } },
Next = new ExecutionGroup
{
Id = "B",
Fields = { new() { Name = "NAME", Value = "B2" } },
Values = { new() { Name = "RESULT", Block = new AnyValueBlock(new GroupResult { Result = "2", Type = GroupResultType.Failed }) } }
}
};
Expand All @@ -27,8 +29,8 @@ public async Task Can_Run_Simple_Groups_Async()

await block.EvaluateAsync(new Context(Site.Object));

Site.Verify(s => s.BeginGroup("A", null), Times.Once);
Site.Verify(s => s.BeginGroup("B", null), Times.Once);
Site.Verify(s => s.BeginGroup("A", "A1"), Times.Once);
Site.Verify(s => s.BeginGroup("B", "B2"), Times.Once);
Site.Verify(s => s.EndGroup(It.IsAny<GroupResult>()), Times.Exactly(2));
Site.Verify(s => s.SingleStepAsync(It.IsAny<Block>()), Times.Exactly(2));
Site.VerifyGet(s => s.Cancellation, Times.Exactly(6));
Expand Down
193 changes: 193 additions & 0 deletions Tests/Engine/GroupAnalyserTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
using BlocklyNet.Core.Model;
using NUnit.Framework;

namespace BlocklyNetTests.Engine;

[TestFixture]
public class GroupAnalyserTests : TestEnvironment
{
private const string Script1 = @"
<xml xmlns=""https://developers.google.com/blockly/xml"">
<block type=""execute_group"" id=""T%p8jx/o/QA6HegV]o:9"" x=""125"" y=""75"">
<field name=""NAME"">Start it all</field>
<field name=""RESULT"">Result</field>
<value name=""RESULT"">
<block type=""group_execution_result"" id=""I[%_mkd9HSpe3K9^!gg("">
<field name=""Type"">Type</field>
<field name=""Result"">Result</field>
<value name=""Type"">
<shadow type=""group_execution_result_type"" id=""|5/]Bs6_2AoOEAkha18K"">
<field name=""VALUE"">Succeeded</field>
</shadow>
</value>
<value name=""Result"">
<block type=""text"" id=""^Q]vG*2T0}N^hL2)|V{H"">
<field name=""TEXT"">1</field>
</block>
</value>
</block>
</value>
<next>
<block type=""execute_group"" id=""At1{sV=Jj]8Y[J_$[i,O"">
<field name=""NAME"">Functions</field>
<field name=""RESULT"">Result</field>
<value name=""RESULT"">
<block type=""procedures_callreturn"" id=""nyM(=w~1AK!y:gPB:W}q"">
<mutation name=""func1""></mutation>
</block>
</value>
<next>
<block type=""execute_group"" id=""vYHa5_xGpuCY%H,,KX)n"">
<field name=""NAME"">End it all</field>
<field name=""RESULT"">Result</field>
<value name=""RESULT"">
<block type=""group_execution_result"" id=""?2si)*W?}FYd0Sm!_NS*"">
<field name=""Type"">Type</field>
<field name=""Result"">Result</field>
<value name=""Type"">
<shadow type=""group_execution_result_type"" id=""I@Rg`jDlM0xC!SyN8htq"">
<field name=""VALUE"">Succeeded</field>
</shadow>
</value>
<value name=""Result"">
<block type=""text"" id=""TC-p|6:-96.ycayK+$.u"">
<field name=""TEXT"">3</field>
</block>
</value>
</block>
</value>
</block>
</next>
</block>
</next>
</block>
<block type=""procedures_defreturn"" id=""@(jK/]}^:m1]yUcVV#`o"" x=""1075"" y=""75"">
<field name=""NAME"">func2</field>
<comment pinned=""false"" h=""80"" w=""160"">Describe this function...</comment>
<statement name=""STACK"">
<block type=""execute_group"" id=""LQmghgu`16o;h6W:MY__"">
<field name=""NAME"">Inner Func</field>
<field name=""RESULT"">Result</field>
<value name=""RESULT"">
<block type=""group_execution_result"" id=""r1qz4JFJLGp57^AFmTy/"">
<field name=""Type"">Type</field>
<field name=""Result"">Result</field>
<value name=""Type"">
<shadow type=""group_execution_result_type"" id=""%1k-H:^tlc_bvX*0:Mc$"">
<field name=""VALUE"">Succeeded</field>
</shadow>
</value>
<value name=""Result"">
<block type=""text"" id=""_S2yF^V;.]]5fwFY;e?]"">
<field name=""TEXT"">2.2.1</field>
</block>
</value>
</block>
</value>
</block>
</statement>
<value name=""RETURN"">
<block type=""group_execution_result"" id="")Hd5MCI,m}70leZY~%4v"">
<field name=""Type"">Type</field>
<field name=""Result"">Result</field>
<value name=""Type"">
<shadow type=""group_execution_result_type"" id=""(:CI_,sf[9*WqRuNvm*f"">
<field name=""VALUE"">Succeeded</field>
</shadow>
</value>
<value name=""Result"">
<block type=""text"" id=""9f~gS0mlqL.`9A!;n?1u"">
<field name=""TEXT"">2.2</field>
</block>
</value>
</block>
</value>
</block>
<block type=""procedures_defreturn"" id=""3l+D=-j5.aUBgEW8mfz7"" x=""825"" y=""325"">
<field name=""NAME"">func1</field>
<comment pinned=""false"" h=""80"" w=""160"">Describe this function...</comment>
<statement name=""STACK"">
<block type=""execute_group"" id=""U,FO=1c.LrZ(F-[qo~[6"">
<field name=""NAME"">Outer Func Start</field>
<field name=""RESULT"">Result</field>
<value name=""RESULT"">
<block type=""group_execution_result"" id=""`fr0|s+3xrm@CN*)OHy{"">
<field name=""Type"">Type</field>
<field name=""Result"">Result</field>
<value name=""Type"">
<shadow type=""group_execution_result_type"" id=""5)-LFdn:4W}QsRZ7*q:d"">
<field name=""VALUE"">Succeeded</field>
</shadow>
</value>
<value name=""Result"">
<block type=""text"" id=""HbwvV;~rWs|H1Dg/rnUb"">
<field name=""TEXT"">2.1</field>
</block>
</value>
</block>
</value>
<next>
<block type=""execute_group"" id=""/;||8fSyn^ZAEqSsJ@$F"">
<field name=""NAME"">Call Inner</field>
<field name=""RESULT"">Result</field>
<value name=""RESULT"">
<block type=""procedures_callreturn"" id=""Isro:p?Y@}U,%L2s(%*-"">
<mutation name=""func2""></mutation>
</block>
</value>
<next>
<block type=""execute_group"" id=""v.+aqR]pxyFqch3fONn^"">
<field name=""NAME"">Outer Func End</field>
<field name=""RESULT"">Result</field>
<value name=""RESULT"">
<block type=""group_execution_result"" id=""MiFbibnRW|lk(zly}4mL"">
<field name=""Type"">Type</field>
<field name=""Result"">Result</field>
<value name=""Type"">
<shadow type=""group_execution_result_type"" id="",j@*_:Em;W5v;ehy;ae8"">
<field name=""VALUE"">Succeeded</field>
</shadow>
</value>
<value name=""Result"">
<block type=""text"" id=""Y;@_7o*xi3$5-52eVF#["">
<field name=""TEXT"">2.3</field>
</block>
</value>
</block>
</value>
</block>
</next>
</block>
</next>
</block>
</statement>
<value name=""RETURN"">
<block type=""group_execution_result"" id=""~p_Yg)Z.nB}2`_Qn6=K4"">
<field name=""Type"">Type</field>
<field name=""Result"">Result</field>
<value name=""Type"">
<shadow type=""group_execution_result_type"" id=""[3^=@4-ZkjY1EdSC39SM"">
<field name=""VALUE"">Succeeded</field>
</shadow>
</value>
<value name=""Result"">
<block type=""text"" id="":iT0(0d!(,~d/$0ox?DO"">
<field name=""TEXT"">2</field>
</block>
</value>
</block>
</value>
</block>
</xml>
";

[Test]
public async Task Can_Retrieve_Group_Structure_With_Functions_Async()
{
var script = Engine.Parser.Parse(Script1);

var tree = await script.GetGroupTreeAsync();

Assert.That(tree, Is.EqualTo(0));
}
}
Loading

0 comments on commit d0de21c

Please sign in to comment.