Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Hyperbee.XS.sln
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hyperbee.Xs.Interactive", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hyperbee.XS.Interactive.Tests", "test\Hyperbee.XS.Interactive.Tests\Hyperbee.XS.Interactive.Tests.csproj", "{92F65113-015D-8683-5CD4-57D748DB3B5D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hyperbee.Xs.Extensions.Lab", "src\Hyperbee.XS.Extensions.Lab\Hyperbee.Xs.Extensions.Lab.csproj", "{21C6B563-32FB-407A-82A9-E63F59A1AEFB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -83,6 +85,10 @@ Global
{92F65113-015D-8683-5CD4-57D748DB3B5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{92F65113-015D-8683-5CD4-57D748DB3B5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{92F65113-015D-8683-5CD4-57D748DB3B5D}.Release|Any CPU.Build.0 = Release|Any CPU
{21C6B563-32FB-407A-82A9-E63F59A1AEFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{21C6B563-32FB-407A-82A9-E63F59A1AEFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{21C6B563-32FB-407A-82A9-E63F59A1AEFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{21C6B563-32FB-407A-82A9-E63F59A1AEFB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
2 changes: 1 addition & 1 deletion src/Hyperbee.XS.Cli/Hyperbee.Xs.Cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FastExpressionCompiler" Version="5.1.1" />
<PackageReference Include="FastExpressionCompiler" Version="5.2.0" />
<PackageReference Include="Spectre.Console" Version="0.50.0" />
<PackageReference Include="Spectre.Console.Cli" Version="0.50.0" />
<PackageReference Include="System.Reflection.Metadata" Version="9.0.4" />
Expand Down
74 changes: 74 additions & 0 deletions src/Hyperbee.XS.Extensions.Lab/FetchParseExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System.Linq.Expressions;
using Hyperbee.Expressions.Lab;
using Hyperbee.XS.Core;
using Hyperbee.XS.Core.Parsers;
using Hyperbee.XS.Core.Writer;
using Parlot.Fluent;

using static Hyperbee.Expressions.Lab.ExpressionExtensions;
using static Parlot.Fluent.Parsers;

namespace Hyperbee.Xs.Extensions.Lab;

public class FetchParseExtension : IParseExtension, IExpressionWriter, IXsWriter
{
public ExtensionType Type => ExtensionType.Expression;
public string Key => "fetch";

public Parser<Expression> CreateParser( ExtensionBinder binder )
{
Parser<Expression> expression = binder.ExpressionParser;
// var response = fetch("name", "URL" );

return If(
ctx => ctx.StartsWith( "(" ),
Between(
Terms.Char( '(' ),
Separated(
Terms.Char( ',' ),
expression
),
Terms.Char( ')' )
)
)
.Then<Expression>( static parts => parts.Count switch
{
4 => Fetch( clientName: parts[0], url: parts[1], method: parts[2], content: parts[3], headers: parts[4] ),
3 => Fetch( clientName: parts[0], url: parts[1], method: parts[2], content: parts[3] ),
_ => Fetch( clientName: parts[0], url: parts[1] )
} )
.Named( "fetch" );
}

public bool CanWrite( Expression node )
{
return node is FetchExpression;
}

public void WriteExpression( Expression node, ExpressionWriterContext context )
{
if ( node is not FetchExpression fetchExpression )
return;

using var writer = context.EnterExpression( "Hyperbee.Expressions.ExpressionExtensions.Lab.Fetch", true, false );

writer.WriteExpression( fetchExpression.ClientName );
writer.Write( ",\n" );
writer.WriteExpression( fetchExpression.Url );
writer.Write( ",\n" );
writer.Write( fetchExpression.Type, indent: true );
}

public void WriteExpression( Expression node, XsWriterContext context )
{
if ( node is not FetchExpression fetchExpression )
return;

using var writer = context.GetWriter();

writer.Write( "fetch(" );
writer.WriteExpression( fetchExpression.ClientName );
writer.WriteExpression( fetchExpression.Url );
writer.Write( ")" );
}
}
56 changes: 56 additions & 0 deletions src/Hyperbee.XS.Extensions.Lab/Hyperbee.Xs.Extensions.Lab.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<IsPackable>true</IsPackable>

<Authors>Stillpoint Software, Inc.</Authors>
<PackageId>Hyperbee.XS.Extensions.Lab</PackageId>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageTags>expressions;script</PackageTags>

<PackageIcon>icon.png</PackageIcon>
<PackageProjectUrl>https://stillpoint-software.github.io/hyperbee.xs/</PackageProjectUrl>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<Copyright>Stillpoint Software, Inc.</Copyright>
<Title>Hyperbee Expression Script [XS] Language Extensions (lab)</Title>
<Description>Sample Expression Script [XS] language extensions.</Description>
<RepositoryUrl>https://github.com/Stillpoint-Software/Hyperbee.XS</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageReleaseNotes>https://github.com/Stillpoint-Software/Hyperbee.XS/releases/latest</PackageReleaseNotes>
<RootNamespace>$(MSBuildProjectName.Replace(" ", "_"))</RootNamespace>
</PropertyGroup>

<ItemGroup>
<None Update="$(MSBuildProjectName).csproj.DotSettings" Visible="false" />
</ItemGroup>

<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>$(AssemblyName).Tests</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>$(AssemblyName).Benchmark</_Parameter1>
</AssemblyAttribute>
</ItemGroup>

<ItemGroup>
<None Include="..\..\assets\icon.png" Pack="true" Visible="false" PackagePath="/" />
<None Include="..\..\LICENSE" Pack="true" Visible="false" PackagePath="/" />
<None Include="..\..\NOTICES" Pack="true" Visible="false" PackagePath="/" />
<None Include="README.md" Pack="true" PackagePath="\" />
<PackageReference Include="Hyperbee.Collections" Version="2.4.0" />
<PackageReference Include="Hyperbee.Expressions" Version="1.2.0" />
<PackageReference Include="Hyperbee.Expressions.Lab" Version="1.2.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" />
<PackageReference Include="Parlot" Version="1.3.6" />
<ProjectReference Include="..\Hyperbee.XS\Hyperbee.XS.csproj" />
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="8.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

</Project>
91 changes: 91 additions & 0 deletions src/Hyperbee.XS.Extensions.Lab/JsonParseExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
using System.Linq.Expressions;
using System.Text.Json;
using Hyperbee.Expressions.Lab;
using Hyperbee.XS.Core;
using Hyperbee.XS.Core.Parsers;
using Hyperbee.XS.Core.Writer;
using Parlot.Fluent;
using static System.Linq.Expressions.Expression;
using static Hyperbee.Expressions.ExpressionExtensions;
using static Hyperbee.Expressions.Lab.ExpressionExtensions;
using static Parlot.Fluent.Parsers;

namespace Hyperbee.Xs.Extensions.Lab;

public class JsonParseExtension : IParseExtension, IExpressionWriter, IXsWriter
{
public ExtensionType Type => ExtensionType.Expression;
public string Key => "json";

public Parser<Expression> CreateParser( ExtensionBinder binder )
{
var expression = binder.ExpressionParser;
// var element = json """{ "first": 1, "second": 2 }"""
// var person = json<Person> """{ "name": "John", "age": 30 }"""

var jsonPathSelect = SkipWhiteSpace( new StringLiteral( '/' ) )
.Then<Expression>( static value => Constant( value.ToString() ) );

return
ZeroOrOne(
Between(
Terms.Char( '<' ),
XsParsers.TypeRuntime(),
Terms.Char( '>' )
)
)
.AndSkip( new WhiteSpaceLiteral( true ) )
.And( expression )
.Then<Expression>( static parts =>
{
var (type, value) = parts;
if ( value.Type == typeof( HttpResponseMessage ) )
return Await( ReadJson( value, type ?? typeof( JsonElement ) ) );

return Expressions.Lab.ExpressionExtensions.Json( value, type );
} )
.And(
ZeroOrOne(
Terms.Text( "::" ).SkipAnd( jsonPathSelect )
)
).Then( static ( ctx, parts ) =>
{
var (json, select) = parts;

return select == null
? json
: JsonPath( json, select );
}
)
.Named( "json" );
}

public bool CanWrite( Expression node )
{
return node is JsonExpression;
}

public void WriteExpression( Expression node, ExpressionWriterContext context )
{
if ( node is not JsonExpression jsonExpression )
return;

using var writer = context.EnterExpression( "Hyperbee.Expressions.Lab.ExpressionExtensions.Json", true, false );

writer.WriteExpression( jsonExpression.InputExpression );
writer.Write( ",\n" );
writer.WriteType( jsonExpression.Type );
}

public void WriteExpression( Expression node, XsWriterContext context )
{
if ( node is not JsonExpression jsonExtension )
return;

using var writer = context.GetWriter();

writer.Write( "json " );
writer.WriteExpression( jsonExtension.InputExpression );
}
}

20 changes: 20 additions & 0 deletions src/Hyperbee.XS.Extensions.Lab/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# XS.Extensions (Lab): Sample Extensions for Hyperbee.XS

### **What is XS?**

[XS](https://github.com/Stillpoint-Software/hyperbee.xs) is a lightweight scripting language designed to simplify and enhance the use of C# expression trees.
It provides a familiar C#-like syntax while offering advanced extensibility, making it a compelling choice for developers
building domain-specific languages (DSLs), rules engines, or dynamic runtime logic systems.

XS.Extensions (Lab) is a collection of sample and proposed extensions for the XS language, including:

- Fetch
- Json
- JsonPath
- Reduce
- Map

## Contributing

We welcome contributions! Please see our [Contributing Guide](https://github.com/Stillpoint-Software/.github/blob/main/.github/CONTRIBUTING.md)
for more details.
56 changes: 56 additions & 0 deletions src/Hyperbee.XS.Extensions.Lab/RegexMatchExpression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System.Linq.Expressions;
using System.Text.RegularExpressions;

namespace Hyperbee.Xs.Extensions.Lab;

public class RegexMatchExpression : Expression
{
public Expression InputExpression { get; }
public Expression Pattern { get; }

public RegexMatchExpression( Expression inputExpression, Expression pattern )
{
InputExpression = inputExpression ?? throw new ArgumentNullException( nameof( inputExpression ) );
Pattern = pattern ?? throw new ArgumentNullException( nameof( pattern ) );
}

public override ExpressionType NodeType => ExpressionType.Extension;
public override Type Type => typeof( MatchCollection );
public override bool CanReduce => true;

protected override Expression VisitChildren( ExpressionVisitor visitor )
{
var visitedInput = visitor.Visit( InputExpression );
var visitedPattern = visitor.Visit( Pattern );

if ( visitedInput != InputExpression || visitedPattern != Pattern )
{
return new RegexMatchExpression( visitedInput, visitedPattern );
}

return this;
}

public override Expression Reduce()
{
var regexMatchesMethod = typeof( Regex )
.GetMethod( nameof( Regex.Matches ), [typeof( string )] )!;

// Use a constructor expression to create the Regex instance
var regexConstructor = typeof( Regex ).GetConstructor( [typeof( string )] )!;

return Call(
New( regexConstructor, Pattern ),
regexMatchesMethod,
InputExpression
);
}
}

public static partial class ExpressionExtensions
{
public static RegexMatchExpression Regex( Expression inputExpression, Expression pattern )
{
return new RegexMatchExpression( inputExpression, pattern );
}
}
Loading
Loading