1.0.3 Dev
This commit is contained in:
Родитель
bbfadec53e
Коммит
3496c28b5d
|
@ -26,7 +26,7 @@ function Build-Nuget {
|
|||
|
||||
[System.IO.File]::WriteAllText($nugetSpecPath, $nugetSpecTemplate.Replace("%PACKAGE_ID%", $packageId).Replace("%ROOT_PATH%", $scriptPath))
|
||||
|
||||
.\nuget.exe pack $nugetSpecPath -basepath $basePath -OutputDirectory bin\release\Guan\Nugets -properties NoWarn=NU5100
|
||||
.\nuget.exe pack $nugetSpecPath -basepath $basePath -OutputDirectory bin\release\Guan\Nugets -properties NoWarn=NU5100,NU5128
|
||||
}
|
||||
|
||||
[string] $scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
|
|
|
@ -0,0 +1,250 @@
|
|||
## Getting Started with Guan
|
||||
|
||||
Guan, which is built as a ```.NET Standard Library```, is designed to be used as part of an existing application (.NET Core, .NET Desktop) where there is a need for logic programming. The consuming program
|
||||
can either house logic rules in files or supply them as string arguments directly to Guan. The purpose of this document is show how to use Guan within an application.
|
||||
|
||||
### Installing Guan
|
||||
|
||||
You can install Guan into an existing application by adding a ```PackageReference``` to the consuming Project's csproj file:
|
||||
|
||||
``` XML
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Logic.Guan" Version="1.0.3" />
|
||||
...
|
||||
</ItemGroup>
|
||||
```
|
||||
### Using Guan from external code
|
||||
Within a code file (.cs) you must reference ```Guan.Logic``` in a using statement:
|
||||
|
||||
```C#
|
||||
using Guan.Logic;
|
||||
```
|
||||
Guan supports the usage of Prolog-like logic rules (textual query expression) that employ the standard format:
|
||||
|
||||
A rule ```head``` which identifies a ```goal``` and series of ```sub-rules (or sub-goals)``` that form the logical workflow.
|
||||
|
||||
```Prolog
|
||||
goal() :- subgoal1, subgoal2
|
||||
```
|
||||
|
||||
In Guan, ```goal``` is implemented as a ```CompoundTerm``` object. It can have any number of arguments (variables), which form the ```CompoundTerm.Arguments``` property, which is a ```List<TermArgument>```. We will revisit this later.
|
||||
|
||||
Let's create a simple program (.NET Core 3.1 Console app) with very simple rules and a few external predicates. You can run this program by building and running the [GuanExamples](/GuanExamples) project.
|
||||
|
||||
```C#
|
||||
using Guan.Logic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GuanExamples
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main()
|
||||
{
|
||||
// External predicate types (as object instances or singletons (static single instances)) must be specified in a Guan FunctorTable object
|
||||
// which is used to create the Module, which is a collection of predicate types and is required by the Query executor (see RunQueryAsync impl in GuanQueryDispatcher.cs).
|
||||
var functorTable = new FunctorTable();
|
||||
functorTable.Add(TestAdditionPredicateType.Singleton("addresult"));
|
||||
functorTable.Add(TestDivisionPredicateType.Singleton("divresult"));
|
||||
functorTable.Add(TestSubtractionPredicateType.Singleton("subresult"));
|
||||
|
||||
// Create a List<string> containing logic rules (these could also be housed in a text file). These rules are very simple by design, as are the external predicates used in this sample console app.
|
||||
// The rules below are simple examples of using logic in their sub rule parts and calling an external predicate.
|
||||
var logicsRules = new List<string>
|
||||
{
|
||||
"test(?x, ?y) :- ?x == ?y, addresult(?x, ?y)",
|
||||
"test(?x, ?y) :- ?y > 0 && ?y < ?x, divresult(?x, ?y)",
|
||||
"test(?x, ?y) :- ?x > ?y, subresult(?x, ?y)",
|
||||
"test1(1)",
|
||||
"test1(2)",
|
||||
"test2(2)",
|
||||
"test2(3)",
|
||||
"test3(q(1, ?x))",
|
||||
"test4(?x, ?y) :- test3(q(?x, ?y)), test1(?y)",
|
||||
"test5(?x, ?y) :- ?x > 1 && ?y < 3, test1(?x), test2(?y)",
|
||||
"test6(?x) :- test1(?x), not(test2(?x))",
|
||||
"test7(?x) :- not(?x < 2), test1(?x)",
|
||||
"test8(?x, ?y, ?z) :- showtype(?x, ?y), ?x = 5, showtype(?x, ?z)",
|
||||
"test9(?x, ?y, ?z) :- b_setval(v1, 0), test1(?x), getval(v1, ?y), b_setval(v1, 5), getval(v1, ?z)",
|
||||
"test10(?x, ?y, ?z) :- b_setval(v1, 0), test1(?x), getval(v1, ?y), setval(v1, 5), getval(v1, ?z)",
|
||||
"showtype(?x, 'var') :- var(?x)",
|
||||
"showtype(?x, 'nonvar') :- nonvar(?x)",
|
||||
"showtype(?x, 'atom') :- atom(?x)",
|
||||
"showtype(?x, 'compound') :- compound(?x)",
|
||||
"f1(?a, ?b, ?b, ?a)"
|
||||
};
|
||||
|
||||
// A Module is a collection of predicate types.
|
||||
Module module = Module.Parse("test", logicsRules, functorTable);
|
||||
var queryDispatcher = new GuanQueryDispatcher(module);
|
||||
|
||||
/* Execute queries via GuanQueryDispatcher helper class */
|
||||
|
||||
// test goal with external predicate impls.
|
||||
queryDispatcher.RunQueryAsync("test(3, 3)").GetAwaiter().GetResult();
|
||||
queryDispatcher.RunQueryAsync("test(0, 0)").GetAwaiter().GetResult();
|
||||
queryDispatcher.RunQueryAsync("test(5, 2)").GetAwaiter().GetResult();
|
||||
queryDispatcher.RunQueryAsync("test(3, 2)").GetAwaiter().GetResult();
|
||||
queryDispatcher.RunQueryAsync("test(4, 2)").GetAwaiter().GetResult();
|
||||
queryDispatcher.RunQueryAsync("test(2, 5)").GetAwaiter().GetResult();
|
||||
queryDispatcher.RunQueryAsync("test(6, 2)").GetAwaiter().GetResult();
|
||||
queryDispatcher.RunQueryAsync("test(8, 2)").GetAwaiter().GetResult();
|
||||
queryDispatcher.RunQueryAsync("test(25, 5)").GetAwaiter().GetResult();
|
||||
queryDispatcher.RunQueryAsync("test(1, 0)").GetAwaiter().GetResult();
|
||||
|
||||
// testx goals with internal predicate impls.
|
||||
// the answer/result for the below query would be (x=1,y=2) given the rules.
|
||||
queryDispatcher.RunQueryAsync("test4(?x, ?y), test2(?y)", true).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
There are three main pieces to executing a Guan query:
|
||||
|
||||
- A repair rule or query expression that Guan will parse and execute.
|
||||
- A Module that contains a list of rules and optionally a ```FunctorTable``` instance that holds a list of predicate types (```FunctorTable``` only applies if your query expressions (rules) employ External Predicates).
|
||||
- An instance of Guan's Query type created via the static Query.Create function, which takes rules, a ```QueryContext``` instance, and a ```ModuleProvider``` instance, which is created with the ```Module(s)``` you define.
|
||||
|
||||
Let's look at the simple helper class in the ```GuanExamples``` project, ```GuanQueryDispatcher```, to make this more clear.
|
||||
|
||||
```C#
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Guan.Logic;
|
||||
|
||||
namespace GuanExamples
|
||||
{
|
||||
public class GuanQueryDispatcher
|
||||
{
|
||||
// This is set by the consumer of this type via this class's constructor.
|
||||
private readonly Module module_;
|
||||
|
||||
public GuanQueryDispatcher(Module module)
|
||||
{
|
||||
module_ = module;
|
||||
}
|
||||
|
||||
public async Task RunQueryAsync(string queryExpression, bool showResult = false)
|
||||
{
|
||||
// Required QueryContext instance.
|
||||
QueryContext queryContext = new QueryContext();
|
||||
|
||||
// Required ModuleProvider instance. You created the module used in its construction in Program.cs.
|
||||
ModuleProvider moduleProvider = new ModuleProvider();
|
||||
moduleProvider.Add(module_);
|
||||
|
||||
// The Query instance that will be used to execute the supplied logic rule, queryExpression arg.
|
||||
Query query = Query.Create(queryExpression, queryContext, moduleProvider);
|
||||
|
||||
// Execute the query.
|
||||
// result will be () if there is no answer/result for supplied query
|
||||
// (see the simple external predicate rules, for example).
|
||||
Term result = await query.GetNextAsync();
|
||||
|
||||
if (showResult)
|
||||
{
|
||||
Console.WriteLine($"answer: {result}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is pretty straightforward and it is expected that you already have experience with ```Prolog```, so let's move on to the External Predicates used in this simple application.
|
||||
|
||||
There are 3 external predicates used here:
|
||||
|
||||
- ```TestAddPredicateType```
|
||||
- ```TestDivPredicateType```
|
||||
- ```TestSubPredicateType```
|
||||
|
||||
Let's look the implementation for ```TestAddPredicateType``` (the other two are exactly the same in structure):
|
||||
|
||||
```C#
|
||||
using Guan.Logic;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GuanExamples
|
||||
{
|
||||
public class TestAddPredicateType : PredicateType
|
||||
{
|
||||
private static TestAddPredicateType Instance;
|
||||
|
||||
class Resolver : BooleanPredicateResolver
|
||||
{
|
||||
public Resolver(CompoundTerm input, Constraint constraint, QueryContext context)
|
||||
: base(input, constraint, context)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Task<bool> CheckAsync()
|
||||
{
|
||||
// This is the value of the first argument passed to the external predicate addresult().
|
||||
long t1 = (long)Input.Arguments[0].Value.GetEffectiveTerm().GetObjectValue();
|
||||
|
||||
// This is the value of the second argument passed to the external predicate addresult().
|
||||
long t2 = (long)Input.Arguments[1].Value.GetEffectiveTerm().GetObjectValue();
|
||||
|
||||
// Do something with argunent values (in this case simply add them together).
|
||||
long result = t1 + t2;
|
||||
|
||||
// Call an external (to Guan) API that does something with the result.
|
||||
Console.WriteLine($"addresult: {result}");
|
||||
|
||||
// BooleanPredicateResolver type always supplies or binds a boolean result.
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static TestAddPredicateType Singleton(string name)
|
||||
{
|
||||
// ??= is C#'s null-coalescing assignment operator. It is convenience syntax that assigns the value of
|
||||
// its right-hand operand to its left-hand operand only if the left-hand operand evaluates to null.
|
||||
// The ??= operator does not evaluate its right-hand operand if the left-hand operand evaluates to non-null.
|
||||
return Instance ??= new TestAddPredicateType(name);
|
||||
}
|
||||
|
||||
// Note the base constructor's arguments minPositionalArguments and maxPositionalArguments.
|
||||
// You control the minimum and maximum number of arguments the predicate supports.
|
||||
// In this case, rules that employ this external predicate must supply only 2 positional arguments.
|
||||
private TestAddPredicateType(string name)
|
||||
: base(name, true, 2, 2)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// override to create the Resolver instance.
|
||||
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
|
||||
{
|
||||
return new Resolver(input, constraint, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that any external predicate must derive from the ```PredicateType``` base class. Further, an external predicate must contain an internal Resolver class that derives from any supported ```Resolver``` type.
|
||||
In this case, all 3 external predicates derive from BooleanPredicateResolver, which means the result of their execution will be bound to a boolean (and in this case, that value is always true).
|
||||
The key function for you here is ```CheckAsync()```. This is basically the entry point to your custom external implementation (external to the rule from which it is called).
|
||||
|
||||
```C#
|
||||
protected override Task<bool> CheckAsync()
|
||||
{
|
||||
// This is the value of the first argument passed to the external predicate addresult().
|
||||
long t1 = (long)Input.Arguments[0].Value.GetEffectiveTerm().GetObjectValue();
|
||||
|
||||
// This is the value of the second argument passed to the external predicate addresult().
|
||||
long t2 = (long)Input.Arguments[1].Value.GetEffectiveTerm().GetObjectValue();
|
||||
|
||||
// Do something with argunent values (in this case simply add them together).
|
||||
long result = t1 + t2;
|
||||
|
||||
// Call an external (to Guan) API that does something with the result.
|
||||
Console.WriteLine($"addresult: {result}");
|
||||
|
||||
// BooleanPredicateResolver type always supplies or binds a boolean result.
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
```
|
||||
|
|
@ -2,12 +2,11 @@
|
|||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata minClientVersion="3.3.0">
|
||||
<id>%PACKAGE_ID%</id>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<releaseNotes>
|
||||
- V1 Release!
|
||||
- Updated documentation.
|
||||
- Added Readme to nupkg.
|
||||
- Code enhancements.
|
||||
- Fixed package dependency bug. When you install Guan into a .NET project, it will also install the required System.Text.Json nupkg.
|
||||
- Updated nuspec file and nupkg generation Powershell script.
|
||||
- Added Getting Started document and related GuanExamples standalone project for use in experimenting with Guan inside a containing .NET Core 3.1 application.
|
||||
</releaseNotes>
|
||||
<authors>Microsoft</authors>
|
||||
<license type="expression">MIT</license>
|
||||
|
@ -21,10 +20,10 @@
|
|||
<files include="**" buildAction="None" copyToOutput="true" />
|
||||
</contentFiles>
|
||||
<dependencies>
|
||||
<group targetFramework=".NETStandard2.0" />
|
||||
<dependency id="System.Text.Json" version="6.0.0" />
|
||||
</dependencies>
|
||||
<projectUrl>https://github.com/microsoft/guan</projectUrl>
|
||||
<tags>guan logic-programming netstandard21 netcore csharp</tags>
|
||||
<tags>guan logic-programming netstandard20 netcore csharp</tags>
|
||||
<copyright>© Microsoft Corporation. All rights reserved.</copyright>
|
||||
</metadata>
|
||||
<files>
|
||||
|
|
7
Guan.sln
7
Guan.sln
|
@ -11,6 +11,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||
ProjectSection(SolutionItems) = preProject
|
||||
Build-Guan.ps1 = Build-Guan.ps1
|
||||
Build-NugetPackage.ps1 = Build-NugetPackage.ps1
|
||||
GettingStarted.md = GettingStarted.md
|
||||
Guan.nuspec.template = Guan.nuspec.template
|
||||
guannuget.md = guannuget.md
|
||||
README.md = README.md
|
||||
|
@ -18,6 +19,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||
SUPPORT.md = SUPPORT.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GuanExamples", "GuanExamples\GuanExamples.csproj", "{94E28450-FF08-4DF9-A014-C3B1E2249FC1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -32,6 +35,10 @@ Global
|
|||
{4042B149-C837-4D0E-8FD9-7C58F9FD5CAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4042B149-C837-4D0E-8FD9-7C58F9FD5CAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4042B149-C837-4D0E-8FD9-7C58F9FD5CAA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{94E28450-FF08-4DF9-A014-C3B1E2249FC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{94E28450-FF08-4DF9-A014-C3B1E2249FC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{94E28450-FF08-4DF9-A014-C3B1E2249FC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{94E28450-FF08-4DF9-A014-C3B1E2249FC1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="System.Text.Json" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace Guan.Logic
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
using System.Text.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Adapater to expose object properties as a compund term using reflection.
|
||||
|
@ -90,7 +90,7 @@ namespace Guan.Logic
|
|||
return "null";
|
||||
}
|
||||
|
||||
return JsonConvert.SerializeObject(this.value, Formatting.Indented);
|
||||
return JsonSerializer.Serialize(this.value, new JsonSerializerOptions { WriteIndented = true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,14 +20,13 @@ namespace Guan.Logic
|
|||
public class QueryContext : IPropertyContext, IWritablePropertyContext
|
||||
{
|
||||
private static long seqGenerator = 0;
|
||||
|
||||
private QueryContext parent;
|
||||
private readonly QueryContext parent;
|
||||
private List<QueryContext> children;
|
||||
private QueryContext root;
|
||||
private readonly QueryContext root;
|
||||
private string orderProperty;
|
||||
private ResolveOrder order;
|
||||
private Dictionary<string, object> variables;
|
||||
private Module asserted;
|
||||
private readonly Dictionary<string, object> variables;
|
||||
private readonly Module asserted;
|
||||
private bool isLocalStable;
|
||||
private bool isStable;
|
||||
private bool isLocalSuspended;
|
||||
|
@ -35,8 +34,8 @@ namespace Guan.Logic
|
|||
private bool isCancelled;
|
||||
private bool enableTrace;
|
||||
private bool enableDebug;
|
||||
private List<IWaitingTask> waiting;
|
||||
private long seq;
|
||||
private readonly List<IWaitingTask> waiting;
|
||||
private readonly long seq;
|
||||
|
||||
public QueryContext()
|
||||
{
|
||||
|
@ -76,7 +75,6 @@ namespace Guan.Logic
|
|||
}
|
||||
|
||||
public event EventHandler Suspended;
|
||||
|
||||
public event EventHandler Resumed;
|
||||
|
||||
public string OrderProperty
|
||||
|
@ -207,8 +205,7 @@ namespace Guan.Logic
|
|||
return this.order;
|
||||
}
|
||||
|
||||
object result;
|
||||
_ = this.variables.TryGetValue(name, out result);
|
||||
_ = this.variables.TryGetValue(name, out object result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -216,9 +213,9 @@ namespace Guan.Logic
|
|||
{
|
||||
if (name == "Order")
|
||||
{
|
||||
if (value is ResolveOrder)
|
||||
if (value is ResolveOrder order1)
|
||||
{
|
||||
this.order = (ResolveOrder)value;
|
||||
this.order = order1;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -358,4 +355,4 @@ namespace Guan.Logic
|
|||
this.waiting.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
using Guan.Logic;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GuanExamples
|
||||
{
|
||||
public class TestAddPredicateType : PredicateType
|
||||
{
|
||||
private static TestAddPredicateType Instance;
|
||||
|
||||
class Resolver : BooleanPredicateResolver
|
||||
{
|
||||
public Resolver(CompoundTerm input, Constraint constraint, QueryContext context)
|
||||
: base(input, constraint, context)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Task<bool> CheckAsync()
|
||||
{
|
||||
// This is the value of the first argument passed to the external predicate addresult().
|
||||
long t1 = (long)Input.Arguments[0].Value.GetEffectiveTerm().GetObjectValue();
|
||||
|
||||
// This is the value of the second argument passed to the external predicate addresult().
|
||||
long t2 = (long)Input.Arguments[1].Value.GetEffectiveTerm().GetObjectValue();
|
||||
|
||||
// Do something with argunent values (in this case simply add them together).
|
||||
long result = t1 + t2;
|
||||
|
||||
// Call an external (to Guan) API that does something with the result.
|
||||
Console.WriteLine($"addresult: {result}");
|
||||
|
||||
// BooleanPredicateResolver type always supplies or binds a boolean result.
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static TestAddPredicateType Singleton(string name)
|
||||
{
|
||||
// ??= is C#'s null-coalescing assignment operator. It is convenience syntax that assigns the value of
|
||||
// its right-hand operand to its left-hand operand only if the left-hand operand evaluates to null.
|
||||
// The ??= operator does not evaluate its right-hand operand if the left-hand operand evaluates to non-null.
|
||||
return Instance ??= new TestAddPredicateType(name);
|
||||
}
|
||||
|
||||
// Note the base constructor's arguments minPositionalArguments and maxPositionalArguments.
|
||||
// You control the minimum and maximum number of arguments the predicate supports.
|
||||
// In this case, rules that employ this external predicate must supply only 2 positional arguments.
|
||||
private TestAddPredicateType(string name)
|
||||
: base(name, true, 2, 2)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// override to create the Resolver instance.
|
||||
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
|
||||
{
|
||||
return new Resolver(input, constraint, context);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
using Guan.Logic;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GuanExamples
|
||||
{
|
||||
public class TestDivPredicateType : PredicateType
|
||||
{
|
||||
private static TestDivPredicateType Instance;
|
||||
|
||||
class Resolver : BooleanPredicateResolver
|
||||
{
|
||||
public Resolver(CompoundTerm input, Constraint constraint, QueryContext context)
|
||||
: base(input, constraint, context)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Task<bool> CheckAsync()
|
||||
{
|
||||
double t1 = Convert.ToDouble(Input.Arguments[0].Value.GetEffectiveTerm().GetObjectValue());
|
||||
double t2 = Convert.ToDouble(Input.Arguments[1].Value.GetEffectiveTerm().GetObjectValue());
|
||||
double result = t1 / t2;
|
||||
Console.WriteLine($"divresult: {result}");
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static TestDivPredicateType Singleton(string name)
|
||||
{
|
||||
return Instance ??= new TestDivPredicateType(name);
|
||||
}
|
||||
|
||||
// Note the base constructor's arguments minPositionalArguments and maxPositionalArguments. You control the minimum and maximum number of arguments the predicate supports.
|
||||
// In this case, rules that employ this external predicate must supply only 2 positional arguments.
|
||||
private TestDivPredicateType(string name)
|
||||
: base(name, true, 2, 2)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
|
||||
{
|
||||
return new Resolver(input, constraint, context);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
using Guan.Logic;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GuanExamples
|
||||
{
|
||||
public class TestSubPredicateType : PredicateType
|
||||
{
|
||||
private static TestSubPredicateType Instance;
|
||||
|
||||
class Resolver : BooleanPredicateResolver
|
||||
{
|
||||
public Resolver(CompoundTerm input, Constraint constraint, QueryContext context)
|
||||
: base(input, constraint, context)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Task<bool> CheckAsync()
|
||||
{
|
||||
long t1 = (long)Input.Arguments[0].Value.GetEffectiveTerm().GetObjectValue();
|
||||
long t2 = (long)Input.Arguments[1].Value.GetEffectiveTerm().GetObjectValue();
|
||||
long result = t1 - t2;
|
||||
Console.WriteLine($"subresult: {result}");
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static TestSubPredicateType Singleton(string name)
|
||||
{
|
||||
return Instance ??= new TestSubPredicateType(name);
|
||||
}
|
||||
|
||||
// Note the base constructor's arguments minPositionalArguments and maxPositionalArguments. You control the minimum and maximum number of arguments the predicate supports.
|
||||
// In this case, rules that employ this external predicate must supply only 2 positional arguments.
|
||||
private TestSubPredicateType(string name)
|
||||
: base(name, true, 2, 2)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
|
||||
{
|
||||
return new Resolver(input, constraint, context);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Logic.Guan" Version="1.0.3" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Guan.Logic;
|
||||
|
||||
namespace GuanExamples
|
||||
{
|
||||
public class GuanQueryDispatcher
|
||||
{
|
||||
private readonly Module module_;
|
||||
|
||||
public GuanQueryDispatcher(Module module)
|
||||
{
|
||||
module_ = module;
|
||||
}
|
||||
|
||||
public async Task RunQueryAsync(string queryExpression, bool showResult = false)
|
||||
{
|
||||
// Required QueryContext instance.
|
||||
QueryContext queryContext = new QueryContext();
|
||||
|
||||
// Required ModuleProvider instance. You created the module used in its construction in Program.cs.
|
||||
ModuleProvider moduleProvider = new ModuleProvider();
|
||||
moduleProvider.Add(module_);
|
||||
|
||||
// The Query instance that will be used to execute the supplied logic rule, queryExpression arg.
|
||||
Query query = Query.Create(queryExpression, queryContext, moduleProvider);
|
||||
|
||||
// Execute the query.
|
||||
// result will be () if there is no answer/result for supplied query (see the simple external predicate rules, for example).
|
||||
Term result = await query.GetNextAsync();
|
||||
|
||||
if (showResult)
|
||||
{
|
||||
Console.WriteLine($"answer: {result}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
using Guan.Logic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GuanExamples
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static async Task Main()
|
||||
{
|
||||
// External predicate types (as object instances or singletons (static single instances)) must be specified in a Guan FunctorTable object
|
||||
// which is used to create the Module, which is a collection of predicate types and is required by the Query executor (see RunQueryAsync impl in GuanQueryDispatcher.cs).
|
||||
var functorTable = new FunctorTable();
|
||||
functorTable.Add(TestAddPredicateType.Singleton("addresult"));
|
||||
functorTable.Add(TestDivPredicateType.Singleton("divresult"));
|
||||
functorTable.Add(TestSubPredicateType.Singleton("subresult"));
|
||||
|
||||
// Create a List<string> containing logic rules (these could also be housed in a text file). These rules are very simple by design, as are the external predicates used in this sample console app.
|
||||
// The rules below are simple examples of using logic in their sub rule parts and calling an external predicate.
|
||||
var logicsRules = new List<string>
|
||||
{
|
||||
"test(?x, ?y) :- ?x == ?y, addresult(?x, ?y)",
|
||||
"test(?x, ?y) :- ?y > 0 && ?y < ?x, divresult(?x, ?y)",
|
||||
"test(?x, ?y) :- ?x > ?y, subresult(?x, ?y)",
|
||||
"test1(1)",
|
||||
"test1(2)",
|
||||
"test2(2)",
|
||||
"test2(3)",
|
||||
"test3(q(1, ?x))",
|
||||
"test4(?x, ?y) :- test3(q(?x, ?y)), test1(?y)",
|
||||
"test5(?x, ?y) :- ?x > 1 && ?y < 3, test1(?x), test2(?y)",
|
||||
"test6(?x) :- test1(?x), not(test2(?x))",
|
||||
"test7(?x) :- not(?x < 2), test1(?x)",
|
||||
"test8(?x, ?y, ?z) :- showtype(?x, ?y), ?x = 5, showtype(?x, ?z)",
|
||||
"test9(?x, ?y, ?z) :- b_setval(v1, 0), test1(?x), getval(v1, ?y), b_setval(v1, 5), getval(v1, ?z)",
|
||||
"test10(?x, ?y, ?z) :- b_setval(v1, 0), test1(?x), getval(v1, ?y), setval(v1, 5), getval(v1, ?z)",
|
||||
"showtype(?x, 'var') :- var(?x)",
|
||||
"showtype(?x, 'nonvar') :- nonvar(?x)",
|
||||
"showtype(?x, 'atom') :- atom(?x)",
|
||||
"showtype(?x, 'compound') :- compound(?x)",
|
||||
"f1(?a, ?b, ?b, ?a)"
|
||||
};
|
||||
|
||||
// A Module is a collection of predicate types.
|
||||
Module module = Module.Parse("test", logicsRules, functorTable);
|
||||
var queryDispatcher = new GuanQueryDispatcher(module);
|
||||
|
||||
/* Execute queries via GuanQueryDispatcher helper class */
|
||||
|
||||
// test goal with external predicate impls.
|
||||
await queryDispatcher.RunQueryAsync("test(3, 3)");
|
||||
await queryDispatcher.RunQueryAsync("test(0, 0)");
|
||||
await queryDispatcher.RunQueryAsync("test(5, 2)");
|
||||
await queryDispatcher.RunQueryAsync("test(3, 2)");
|
||||
await queryDispatcher.RunQueryAsync("test(4, 2)");
|
||||
await queryDispatcher.RunQueryAsync("test(2, 5)");
|
||||
await queryDispatcher.RunQueryAsync("test(6, 2)");
|
||||
await queryDispatcher.RunQueryAsync("test(8, 2)");
|
||||
await queryDispatcher.RunQueryAsync("test(25, 5)");
|
||||
await queryDispatcher.RunQueryAsync("test(1, 0)");
|
||||
|
||||
// testx goals with internal predicate impls.
|
||||
// the answer/result for the below query would be (x=1,y=2) given the rules.
|
||||
await queryDispatcher.RunQueryAsync("test4(?x, ?y), test2(?y)", true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -256,6 +256,11 @@ namespace GuanTest
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the supplied command from a .test file.
|
||||
/// </summary>
|
||||
/// <param name="command">Command to run.</param>
|
||||
/// <returns></returns>
|
||||
public override bool ExecuteCommand(string command)
|
||||
{
|
||||
List<string> args = ParseCommand(command);
|
||||
|
|
|
@ -184,6 +184,11 @@ namespace GuanTest
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a command from a .test file.
|
||||
/// </summary>
|
||||
/// <param name="command">Command to parse.</param>
|
||||
/// <returns>A list of strings that contain command identifiers.</returns>
|
||||
protected static List<string> ParseCommand(string command)
|
||||
{
|
||||
List<string> result = new List<string>();
|
||||
|
|
|
@ -8,6 +8,10 @@ Guan employs Prolog style syntax for writing logic rules. It enables easy intero
|
|||
|
||||
Author: Lu Xun, Microsoft.
|
||||
|
||||
### Getting Started
|
||||
|
||||
Please see the [Getting Started](./GettingStarted.md) section to play around with Guan in a sample application, ```GuanExamples``` with very simple rules, simple External Predicate implementations, and highly documented sample code.
|
||||
|
||||
### Syntax
|
||||
|
||||
As stated above, Guan uses [Prolog style syntax](http://www.learnprolognow.org/index.php). We will not describe things that are common with standard Prolog, but rather present the differences below (they are different mostly to allow more natural interop between rules and C# code):
|
||||
|
|
Загрузка…
Ссылка в новой задаче