This commit is contained in:
Charles Torre 2022-01-20 16:19:23 -08:00
Родитель bbfadec53e
Коммит 3496c28b5d
16 изменённых файлов: 559 добавлений и 24 удалений

Просмотреть файл

@ -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

250
GettingStarted.md Normal file
Просмотреть файл

@ -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>

Просмотреть файл

@ -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}");
}
}
}
}

67
GuanExamples/Program.cs Normal file
Просмотреть файл

@ -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):