This commit is contained in:
Charles Torre 2020-08-28 14:19:02 -07:00
Родитель cb4f307eb4
Коммит b1b3cab2c1
86 изменённых файлов: 12569 добавлений и 20 удалений

63
.gitattributes поставляемый Normal file
Просмотреть файл

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

30
.gitignore поставляемый
Просмотреть файл

@ -13,9 +13,6 @@
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
@ -29,7 +26,6 @@ bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
@ -43,10 +39,9 @@ Generated\ Files/
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
# NUNIT
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
@ -127,6 +122,9 @@ _ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
@ -184,8 +182,6 @@ PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
@ -210,8 +206,6 @@ BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
@ -261,9 +255,7 @@ ServiceFabricBackup/
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
*- Backup*.rdl
# Microsoft Fakes
FakesAssemblies/
@ -299,6 +291,10 @@ paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush personal settings
.cr/personal
@ -341,10 +337,4 @@ ASALocalRun/
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
healthchecksdb

25
Guan.sln Normal file
Просмотреть файл

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30011.22
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Guan", "Guan\Guan.csproj", "{211FF6EB-91CE-4B89-9543-5304834066E3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{211FF6EB-91CE-4B89-9543-5304834066E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{211FF6EB-91CE-4B89-9543-5304834066E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{211FF6EB-91CE-4B89-9543-5304834066E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{211FF6EB-91CE-4B89-9543-5304834066E3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FE9954AB-2A53-483D-9E0B-9B6E92C26B0F}
EndGlobalSection
EndGlobal

83
Guan/Common/AddFunc.cs Normal file
Просмотреть файл

@ -0,0 +1,83 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.
//
// @File: AddFunc.cs
//
// @Owner: xunlu
// @Test: xunlu
//
// Purpose:
// Function to add integer or concatenate string.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
using System;
namespace Guan.Common
{
internal class AddFunc : StandaloneFunc
{
public static readonly AddFunc Singleton = new AddFunc();
private MathsFunc mathsAdd_;
private AddFunc()
: base("add")
{
mathsAdd_ = new MathsFunc("add", '+');
}
public override object Invoke(object[] args)
{
if (args.Length == 2)
{
if (args[0] is DateTime)
{
return AddDateTime((DateTime)args[0], args[1]);
}
if (args[0] is GuanTime)
{
return AddGuanTime((GuanTime)args[0], args[1]);
}
object result = mathsAdd_.Invoke(args);
if (result != null)
{
return result;
}
}
string s = "";
foreach (object arg in args)
{
if (arg != null)
{
s += arg.ToString();
}
}
return s;
}
private DateTime AddDateTime(DateTime arg1, object arg2)
{
if (arg2 is TimeSpan)
{
return arg1 + (TimeSpan)arg2;
}
if (arg2 is string)
{
return arg1 + TimeSpan.Parse((string)arg2);
}
throw new ArgumentException("Invalid argument to add to DateTime: " + arg2);
}
private GuanTime AddGuanTime(GuanTime arg1, object arg2)
{
DateTime result = AddDateTime(arg1.Time, arg2);
return new GuanTime(result);
}
}
}

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

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
namespace Guan.Common
{
public static class AutoLoadResource
{
public static void LoadResources(Type resourceType)
{
MethodInfo addMethod = resourceType.GetMethod("Add", BindingFlags.Static | BindingFlags.Public);
Assembly thisAssembly = Assembly.GetExecutingAssembly();
LoadAssembly(thisAssembly, resourceType, addMethod);
HashSet<string> assemblies = new HashSet<string>();
string externalAssemblies = Utility.GetConfig("ExternalAssemblies", string.Empty);
if (!string.IsNullOrEmpty(externalAssemblies))
{
foreach (string name in externalAssemblies.Split(','))
{
assemblies.Add(name);
}
assemblies.Add("Guan.Tracing.dll");
assemblies.Add("Guan.dll");
}
string assemblyPath = thisAssembly.Location;
string directoryPath = Path.GetDirectoryName(assemblyPath);
foreach (string path in Directory.GetFiles(directoryPath, "*.dll"))
{
if (assemblies.Count == 0 || assemblies.Contains(Path.GetFileName(path)))
{
try
{
Assembly assembly = Assembly.LoadFrom(path);
LoadAssembly(assembly, resourceType, addMethod);
}
catch (BadImageFormatException)
{
}
catch (FileLoadException)
{
}
catch (ReflectionTypeLoadException)
{
}
}
}
}
private static void LoadAssembly(Assembly assembly, Type resourceType, MethodInfo addMethod)
{
foreach (Type type in assembly.GetTypes())
{
BindingFlags flags = BindingFlags.Static | BindingFlags.Public;
FieldInfo[] fields = type.GetFields(flags);
foreach (FieldInfo field in fields)
{
if (field.FieldType.IsSubclassOf(resourceType))
{
LoadMember(type, field.Name, field.FieldType, BindingFlags.GetField, addMethod);
}
}
PropertyInfo[] properties = type.GetProperties(flags);
foreach (PropertyInfo property in properties)
{
if (property.PropertyType == resourceType)
{
LoadMember(type, property.Name, property.PropertyType, BindingFlags.GetProperty, addMethod);
}
}
}
}
private static void LoadMember(Type type, string name, Type memberType, BindingFlags flags, MethodInfo addMethod)
{
object instance = type.InvokeMember(name, flags, null, null, null, CultureInfo.InvariantCulture);
addMethod.Invoke(null, new object[] { instance });
}
}
}

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

@ -0,0 +1,58 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Guan.Common
{
public abstract class CancellableTaskSource<T>
{
private TaskCompletionSource<T> source_;
public CancellableTaskSource(CancellationToken cancellationToken)
{
source_ = new TaskCompletionSource<T>();
cancellationToken.Register(() => { Cancel(); });
}
protected abstract void OnCancelled();
public Task<T> Task
{
get
{
return source_.Task;
}
}
private void Cancel()
{
if (source_.TrySetCanceled())
{
System.Threading.Tasks.Task.Run(() => OnCancelled());
}
}
public bool Complete(T result)
{
return source_.TrySetResult(result);
}
public void Complete(Exception e)
{
source_.SetException(e);
}
}
public abstract class CancellableTaskSource : CancellableTaskSource<bool>
{
public CancellableTaskSource(CancellationToken cancellationToken)
: base(cancellationToken)
{
}
public bool Complete()
{
return Complete(true);
}
}
}

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

@ -0,0 +1,65 @@
using System.Collections.Generic;
namespace Guan.Common
{
public class CompositePropertyContext : IPropertyContext
{
private List<IPropertyContext> contexts_;
public CompositePropertyContext(IPropertyContext context, params IPropertyContext[] extraContexts)
{
contexts_ = new List<IPropertyContext>(extraContexts.Length + 1)
{
context
};
contexts_.AddRange(extraContexts);
}
public void SetContext(IPropertyContext context, int index = 0)
{
while (contexts_.Count < index)
{
contexts_.Add(null);
}
if (index == contexts_.Count)
{
contexts_.Add(context);
}
else
{
contexts_[index] = context;
}
}
public virtual object this[string name]
{
get
{
int start, end;
if (name.Length > 3 && name[0] == '#' && name[2] == ':' && name[1] >= '0' && name[1] <= '9')
{
start = name[1] - '0';
end = start + 1;
name = name.Substring(3);
}
else
{
start = 0;
end = contexts_.Count;
}
for (int i = start; i < end; i++)
{
object result = contexts_[i][name];
if (result != null)
{
return result;
}
}
return null;
}
}
}
}

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

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
namespace Guan.Common
{
public class ConsoleSink : IEventSink
{
public static ConsoleSink Singleton = new ConsoleSink();
private static ConsoleColor s_oldColor = Console.ForegroundColor;
private static Dictionary<EventType, ConsoleColor> s_colorMapping = GetColorMapping();
private static Dictionary<EventType, ConsoleColor> GetColorMapping()
{
Dictionary<EventType, ConsoleColor> map = new Dictionary<EventType, ConsoleColor>();
AddColor(map, EventType.Error, ConsoleColor.Red);
AddColor(map, EventType.Warning, ConsoleColor.Yellow);
AddColor(map, EventType.Info1, ConsoleColor.Cyan);
AddColor(map, EventType.Info2, ConsoleColor.Green);
return map;
}
private static void AddColor(Dictionary<EventType, ConsoleColor> map,
EventType key, ConsoleColor color)
{
int bc = (int) Console.BackgroundColor;
bool bDark = (bc <= 7);
int fc = (int) color;
if (bDark)
{
fc |= 0x08;
}
else
{
fc &= 0x07;
}
map[key] = (ConsoleColor) fc;
}
private ConsoleSink()
{
}
public void WriteEntry(string src, EventType msgType, string msgText)
{
lock (s_colorMapping)
{
ConsoleColor msgColor = s_oldColor;
try
{
if (s_colorMapping.TryGetValue(msgType, out msgColor))
{
Console.ForegroundColor = msgColor;
}
Console.WriteLine(msgText);
}
finally
{
if (msgColor != s_oldColor)
{
Console.ForegroundColor = s_oldColor;
}
}
}
}
}
}

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

@ -0,0 +1,35 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.
//
// @File: EmptyPropertyContext.cs
//
// @Owner: xunlu
// @Test: xunlu
//
// Purpose:
// Property context that does not have any property.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Guan.Common
{
/// <summary>
/// Property context that does not have any property.
/// </summary>
public class EmptyPropertyContext : IPropertyContext
{
public static readonly EmptyPropertyContext Singleton = new EmptyPropertyContext();
private EmptyPropertyContext()
{
}
object IPropertyContext.this[string name]
{
get
{
return null;
}
}
}
}

193
Guan/Common/EventLog.cs Normal file
Просмотреть файл

@ -0,0 +1,193 @@
using System;
using System.Globalization;
using System.Collections.Generic;
namespace Guan.Common
{
public enum EventType
{
Error,
Warning,
Info,
Info1,
Info2,
Verbose
}
internal interface IEventSink
{
void WriteEntry(string src, EventType msgType, string msgText);
};
public class EventLog
{
private class SinkWrapper
{
private IEventSink m_sink;
private int m_defaultLevel;
private int m_maxTraceLevel;
private Dictionary<string, int> m_sourceOverride;
public SinkWrapper(IEventSink sink, int defaultLevel, string sourceOverride)
{
m_sink = sink;
m_defaultLevel = defaultLevel;
m_maxTraceLevel = defaultLevel;
if (!string.IsNullOrEmpty(sourceOverride))
{
m_sourceOverride = new Dictionary<string, int>();
foreach (string entry in sourceOverride.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
int index = entry.IndexOf('=');
if (index > 0)
{
string key = entry.Substring(0, index).Trim();
string value = entry.Substring(index + 1).Trim();
int overrideLevel;
if (int.TryParse(value, out overrideLevel))
{
m_sourceOverride[key] = overrideLevel;
if (overrideLevel > m_maxTraceLevel)
{
m_maxTraceLevel = overrideLevel;
}
}
}
}
}
}
public IEventSink Sink
{
get
{
return m_sink;
}
}
public void WriteEntry(int msgLevel, string src, EventType msgType, string msgText, params object[] args)
{
if (msgLevel > m_maxTraceLevel)
{
return;
}
int logLevel = m_defaultLevel;
if (m_sourceOverride != null)
{
string effectiveSrc = src;
while (!m_sourceOverride.TryGetValue(effectiveSrc, out logLevel))
{
int lastSegIndex = effectiveSrc.LastIndexOf('.');
if (lastSegIndex > 0)
{
effectiveSrc = effectiveSrc.Substring(0, lastSegIndex);
}
else
{
logLevel = m_defaultLevel;
break;
}
}
}
if (msgLevel <= logLevel)
{
string outputText = string.Format(CultureInfo.InvariantCulture, msgText, args);
m_sink.WriteEntry(src, msgType, outputText);
}
}
}
private static List<SinkWrapper> s_sinks = CreateSinks(new List<SinkWrapper>());
private static List<SinkWrapper> CreateSinks(List<SinkWrapper> current)
{
List<SinkWrapper> sinks = new List<SinkWrapper>();
int consoleLevel = Utility.GetConfig("ConsoleSink.DefaultLevel", 3);
if (consoleLevel >= 0)
{
sinks.Add(new SinkWrapper(ConsoleSink.Singleton, consoleLevel, Utility.GetConfig("ConsoleSink.Overrides")));
}
int fileLevel = Utility.GetConfig("FileSink.DefaultLevel", 4);
if (fileLevel >= 0)
{
IEventSink fileSink = null;
foreach (SinkWrapper sink in current)
{
if (sink.Sink is FileEventSink)
{
fileSink = sink.Sink;
}
}
if (fileSink == null)
{
fileSink = new FileEventSink("guan", null);
}
sinks.Add(new SinkWrapper(fileSink, fileLevel, Utility.GetConfig("FileSink.Overrides")));
}
return sinks;
}
public static void RefreshConfig()
{
s_sinks = CreateSinks(s_sinks);
}
public static void WriteError(string src, string format, params object[] args)
{
foreach (SinkWrapper sink in s_sinks)
{
sink.WriteEntry(1, src, EventType.Error, format, args);
}
}
public static void WriteWarning(string src, string format, params object[] args)
{
foreach (SinkWrapper sink in s_sinks)
{
sink.WriteEntry(2, src, EventType.Warning, format, args);
}
}
public static void WriteInfo(string src, string format, params object[] args)
{
foreach (SinkWrapper sink in s_sinks)
{
sink.WriteEntry(3, src, EventType.Info, format, args);
}
}
public static void WriteInfo1(string src, string format, params object[] args)
{
foreach (SinkWrapper sink in s_sinks)
{
sink.WriteEntry(3, src, EventType.Info1, format, args);
}
}
public static void WriteInfo2(string src, string format, params object[] args)
{
foreach (SinkWrapper sink in s_sinks)
{
sink.WriteEntry(3, src, EventType.Info2, format, args);
}
}
public static void WriteVerbose(string src, string format, params object[] args)
{
foreach (SinkWrapper sink in s_sinks)
{
sink.WriteEntry(4, src, EventType.Verbose, format, args);
}
}
}
}

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

@ -0,0 +1,84 @@
using System;
using System.IO;
using System.Globalization;
using System.Threading;
namespace Guan.Common
{
internal sealed class FileEventSink : IEventSink
{
private TextWriter m_fs;
private string m_fileName;
private string m_formatString;
private string m_logName;
public FileEventSink(string logName, string fileNameFormat)
{
m_logName = logName;
m_formatString = fileNameFormat;
}
private void cleanup()
{
if (m_fs != null)
{
try
{
m_fs.Close();
}
catch (IOException)
{
}
catch (ObjectDisposedException)
{
}
m_fs = null;
}
}
public void WriteEntry(string src, EventType msgType, string msgText)
{
if (msgText == null)
{
return;
}
lock (this)
{
if (m_fs == null || m_formatString != null)
{
string newFileName;
if (m_formatString != null)
{
newFileName = m_logName + DateTime.Now.ToString(m_formatString, CultureInfo.InvariantCulture) + ".trace";
if (newFileName != m_fileName)
{
cleanup();
m_fileName = newFileName;
}
}
else
{
newFileName = m_logName + ".trace";
}
if (m_fs == null)
{
m_fs = new StreamWriter(File.Open(newFileName, FileMode.Create, FileAccess.Write, FileShare.ReadWrite));
}
}
msgText = msgText.Replace("\n", "\t");
try
{
m_fs.WriteLine("{0},{1},{2},{3},{4}", Utility.FormatTime(DateTime.Now), msgType, Thread.CurrentThread.ManagedThreadId, src, msgText);
m_fs.Flush();
}
catch (IOException)
{
}
}
}
}
}

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

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(EnvironmentConfig)" />
<Import Project="$(SRCROOT)\properties\service_fabric_managed_prod.props" />
<PropertyGroup>
<ProjectGuid>{0EC83CD5-2733-41E1-AFFA-A32F54C9D38A}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>Guan.Common</RootNamespace>
<AssemblyName>Guan.Common</AssemblyName>
<AssemblyTitle>Guan.Common</AssemblyTitle>
</PropertyGroup>
<PropertyGroup>
<OutputPath>$(BaseDir)\out\</OutputPath>
<OutDir>$(OutputPath)</OutDir>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.configuration" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AddFunc.cs" />
<Compile Include="AutoLoadResource.cs" />
<Compile Include="CancellableTaskSource.cs" />
<Compile Include="CompositePropertyContext.cs" />
<Compile Include="ConsoleSink.cs" />
<Compile Include="EmptyPropertyContext.cs" />
<Compile Include="EventLog.cs" />
<Compile Include="FileEventSink.cs" />
<Compile Include="GuanObject.cs" />
<Compile Include="GuanObjectTransformSpecification.cs" />
<Compile Include="GuanTime.cs" />
<Compile Include="Heap.cs" />
<Compile Include="IConfigurable.cs" />
<Compile Include="IPartialOrder.cs" />
<Compile Include="IPropertyContext.cs" />
<Compile Include="MathsFunc.cs" />
<Compile Include="PropertyMatchFunc.cs" />
<Compile Include="Range.cs" />
<Compile Include="ReleaseAssert.cs" />
<Compile Include="GuanException.cs" />
<Compile Include="GuanExpression.cs" />
<Compile Include="GuanFunc.cs" />
<Compile Include="GuanPredicate.cs" />
<Compile Include="SplitFunc.cs" />
<Compile Include="StringFunc.cs" />
<Compile Include="TextConfigFile.cs" />
<Compile Include="TimeRange.cs" />
<Compile Include="TimeRangeList.cs" />
<Compile Include="Tri.cs" />
<Compile Include="Utility.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

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

@ -0,0 +1,57 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.
//
// @File: GuanException.cs
//
// @Owner: xunlu
// @Test: xunlu
//
// Purpose:
// Define base exception generated from the trace tool.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
using System;
using System.Runtime.Serialization;
namespace Guan.Common
{
/// <summary>
/// Base exception generated from the trace tool.
/// </summary>
[Serializable]
public class GuanException : Exception
{
/// <summary>
/// Default constructor.
/// </summary>
public GuanException()
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="message">A message that describes the error.</param>
public GuanException(string message, params object[] args)
: base(string.Format(message, args))
{
}
public GuanException(Exception inner, string message, params object[] args)
: base(string.Format(message, args), inner)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="info">The object that holds the serialized object data.</param>
/// <param name="context">The contextual information about the source
/// or destination.</param>
protected GuanException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

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

@ -0,0 +1,855 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.
//
// @File: GuanExpression.cs
//
// @Owner: xunlu
// @Test: xunlu
//
// Purpose:
// Wrapper for expression used in trace.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
using System;
using System.Text;
using System.Globalization;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Guan.Common
{
public interface IGuanExpressionContext
{
GuanFunc GetFunc(string name);
}
/// <summary>
/// Expression tree for manipulating properties. It is usually
/// generated from a string expression although it is also possible
/// to manually construct the tree.
/// Every expression tree node is basically a GuanFunc and
/// its child nodes are the arguments to the function.
/// Not thread safe.
/// </summary>
public class GuanExpression
{
/// <summary>
/// Operators that can appear in the string expression.
/// </summary>
public class Operator
{
private string m_op;
private GuanFunc m_func;
private int m_level;
public Operator(string op, GuanFunc func, int level)
{
m_op = op;
m_func = func;
m_level = level;
}
internal string Op
{
get
{
return m_op;
}
}
internal GuanFunc Func
{
get
{
return m_func;
}
}
internal int Level
{
get
{
return m_level;
}
}
internal bool Match(string exp, ref int start)
{
if (start + m_op.Length > exp.Length)
{
return false;
}
for (int i = 0; i < m_op.Length; i++)
{
if (m_op[i] != exp[start + i])
{
return false;
}
}
start += m_op.Length;
return true;
}
public override string ToString()
{
return m_op;
}
}
class NamedContext : IPropertyContext
{
private IPropertyContext original_;
private string name_;
public NamedContext(IPropertyContext context, string name)
{
original_ = context;
name_ = name + ":";
}
public object this[string name]
{
get
{
if (!name.StartsWith(name_))
{
return "<" + name + ">";
}
return original_[name.Substring(name_.Length)];
}
}
}
private GuanFunc m_func;
private List<GuanExpression> m_children;
private static readonly List<GuanExpression> s_nullChildren = new List<GuanExpression>();
private static readonly Operator s_sentinel = new Operator("Dummy", Literal.Empty, 0);
private static Tri<Operator> s_operators = CreateOperators();
private static readonly Regex s_evalProperty = new Regex(@"(\<[\w#:.()/]+\>)", RegexOptions.Compiled);
public GuanExpression(GuanExpression other)
{
m_func = other.m_func;
if (other.m_children == s_nullChildren)
{
m_children = s_nullChildren;
}
else
{
m_children = new List<GuanExpression>(other.m_children.Count);
foreach (GuanExpression child in other.m_children)
{
m_children.Add(new GuanExpression(child));
}
}
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="func">The function associated with this node.</param>
public GuanExpression(GuanFunc func)
{
m_func = func;
m_children = s_nullChildren;
}
public GuanExpression(GuanFunc func, List<GuanExpression> children)
{
m_func = func;
m_children = children;
}
public GuanExpression(object literalValue)
{
m_func = new Literal(literalValue);
m_children = s_nullChildren;
}
public GuanExpression Clone()
{
List<GuanExpression> children = new List<GuanExpression>();
foreach (GuanExpression expression in m_children)
{
children.Add(expression.Clone());
}
return new GuanExpression(m_func, children);
}
/// <summary>
/// The function for the expression.
/// </summary>
public GuanFunc Func
{
get
{
return m_func;
}
}
/// <summary>
/// Whether the expression is a literal value.
/// </summary>
public bool IsLiteral
{
get
{
return (m_func is Literal);
}
}
/// <summary>
/// The list of argument expression.
/// </summary>
public IList<GuanExpression> Children
{
get
{
return m_children;
}
}
/// <summary>
/// Add a child node (argument) to the expression
/// tree node. Note that the order of the children
/// is usually important as it determines the order
/// of the arguments to the function.
/// </summary>
/// <param name="child">The child node.</param>
public void AddChild(GuanExpression child)
{
if (m_children == s_nullChildren)
{
m_children = new List<GuanExpression>();
}
m_children.Add(child);
return;
}
/// <summary>
/// Evaluate the expression within the given context.
/// </summary>
/// <param name="context">The context object.</param>
/// <returns>Result of the evaluation.</returns>
public object Evaluate(IPropertyContext context)
{
try
{
return m_func.Invoke(context, m_children);
}
catch (Exception e)
{
string message = string.Format(CultureInfo.InvariantCulture,
"Error evaluating the expression {0}: {1}",
this, e.Message);
throw new ExpressionException(message, e);
}
}
public GuanExpression EvaluateExpression(IPropertyContext context, string contextName, bool isFinal, IGuanExpressionContext expressionContext = null)
{
try
{
string expression = (string) m_func.Invoke(new NamedContext(context, contextName), m_children);
return GuanExpression.Build(isFinal ? expression : "{" + expression + "}", expressionContext);
}
catch (Exception e)
{
string message = string.Format(CultureInfo.InvariantCulture,
"Error evaluating the expression {0}: {1}",
this, e.Message);
throw new ExpressionException(message, e);
}
}
public GuanPredicate EvaluatePredicate(IPropertyContext context, string contextName, IGuanExpressionContext expressionContext = null)
{
GuanExpression expression = EvaluateExpression(context, contextName, true, expressionContext);
return new GuanPredicate(expression);
}
/// <summary>
/// Optimize the expression by binding literal arguments to
/// functions.
/// </summary>
internal void Bind()
{
bool isLiteral = true;
if (m_children != s_nullChildren)
{
for (int i = 0; i < m_children.Count; i++)
{
m_children[i].Bind();
isLiteral = isLiteral && (m_children[i].m_func is Literal);
}
m_func = m_func.Bind(m_children);
}
// Convert to literal if possible.
if (isLiteral && (m_func is StandaloneFunc) && !(m_func is Literal))
{
m_func = new Literal(Evaluate(null));
}
if (m_func is Literal)
{
m_children = s_nullChildren;
}
}
internal bool GetLiteral<T>(out T result)
{
if (m_func is Literal)
{
result = (T) Evaluate(null);
return true;
}
else
{
result = default(T);
return false;
}
}
/// <summary>
/// String representation of the object.
/// </summary>
/// <returns>String representation of the object.</returns>
public override string ToString()
{
StringBuilder result = new StringBuilder(1024);
result.AppendFormat("{0}", m_func);
if (m_children.Count != 0)
{
result.Append("(");
foreach (GuanExpression exp in m_children)
{
result.AppendFormat("{0},", exp);
}
result.Length--;
result.Append(")");
}
return result.ToString();
}
private static Tri<Operator> CreateOperators()
{
Tri<Operator> result = new Tri<Operator>();
AddOperator(result, new Operator("(", Literal.Empty, 1));
AddOperator(result, new Operator(")", Literal.Empty, 1));
AddOperator(result, new Operator("||", OrFunc.Singleton, 2));
AddOperator(result, new Operator("&&", AndFunc.Singleton, 3));
AddOperator(result, new Operator(">", ComparisonFunc.GT, 4));
AddOperator(result, new Operator("<", ComparisonFunc.LT, 4));
AddOperator(result, new Operator(">=", ComparisonFunc.GE, 4));
AddOperator(result, new Operator("<=", ComparisonFunc.LE, 4));
AddOperator(result, new Operator("!", NotFunc.Singleton, 7));
AddOperator(result, new Operator("%", GetFunc.Singleton, 7));
AddOperator(result, new Operator("~~", MatchFunc.Singleton, 4));
AddOperator(result, new Operator("=", PropertyMatchFunc.Equal, 4));
AddOperator(result, new Operator("==", ComparisonFunc.EQ, 4));
AddOperator(result, new Operator("!=", ComparisonFunc.NE, 4));
AddOperator(result, new Operator("+", AddFunc.Singleton, 5));
AddOperator(result, new Operator("-", MathsFunc.Minus, 5));
AddOperator(result, new Operator("*", MathsFunc.Multiply, 6));
AddOperator(result, new Operator("/", MathsFunc.Divide, 6));
return result;
}
private static void AddOperator(Tri<Operator> operators, Operator op)
{
operators.Add(op.Op, op);
}
private static void SetOperator(List<Operator> table, Operator propertyOperator)
{
int len = propertyOperator.Op.Length;
int i;
for (i = 0; i < table.Count && len <= table[i].Op.Length; i++)
{
if (table[i].Op == propertyOperator.Op)
{
table[i] = propertyOperator;
return;
}
}
table.Insert(i, propertyOperator);
}
private static Operator ReadOperator(string exp, ref int start)
{
return s_operators.Get(exp, ref start);
}
private static int SkipQuote(string exp, char quote, int index)
{
int i = index;
while (++i < exp.Length)
{
if (exp[i] == quote)
{
return i;
}
else if (exp[i] == '\\')
{
i++;
}
}
return index;
}
private static object ReadLiteral(string exp, char quote)
{
StringBuilder result = new StringBuilder(exp.Length);
for (int i = 0; i < exp.Length; i++)
{
if (exp[i] != quote)
{
if (exp[i] == '\\' && (i + 1) < exp.Length)
{
i++;
result.Append(exp[i]);
}
else
{
result.Append(exp[i]);
}
}
}
string value = result.ToString();
if (quote == '\0')
{
long intValue;
if (long.TryParse(value, out intValue))
{
return intValue;
}
}
return value;
}
private static GuanExpression ReadFunc(string exp, string name, IGuanExpressionContext expressionContext, ref int start)
{
GuanFunc func = GuanFunc.Get(name, expressionContext);
if (func == null)
{
throw new ArgumentException("function not found: " + name);
}
GuanExpression result = new GuanExpression(func);
int level = 0;
for (int i = start; i < exp.Length; i++)
{
char c = exp[i];
if (c == '(')
{
level++;
}
else if (c == '"' || c == '\'')
{
i = SkipQuote(exp, c, i);
}
else if ((c == ',' || c == ')') && level == 0)
{
string argExp = exp.Substring(start, i - start).Trim();
if (argExp.Length > 0)
{
GuanExpression child = Build(argExp, expressionContext);
result.AddChild(child);
}
else if (c == ',' || result.Children.Count > 0)
{
result.AddChild(new GuanExpression(new Literal(null)));
}
start = i + 1;
}
if (c == ')')
{
if (level == 0)
{
start = i + 1;
return result;
}
level--;
}
}
throw new ArgumentException("Invalid function expression: " + exp);
}
private static GuanExpression ReadToken(string exp, IGuanExpressionContext expressionContext, ref int start)
{
if (start >= exp.Length)
{
return null;
}
char quote = '\0';
int i;
for (i = start; i < exp.Length; i++)
{
char c = exp[i];
// Operators are always non-alphanumerical
if (!char.IsLetterOrDigit(c))
{
if (c == '"' || c== '\'')
{
quote = c;
i = SkipQuote(exp, c, i);
}
else
{
if (c == '(')
{
// A '(' without leading characters is not a function
// but an operator.
string name = exp.Substring(start, i - start).Trim();
if (name.Length > 0)
{
start = i + 1;
return ReadFunc(exp, name, expressionContext, ref start);
}
}
if (c == '-')
{
continue;
}
int j = i;
if (ReadOperator(exp, ref j) != null)
{
break;
}
}
}
}
string token = exp.Substring(start, i - start).Trim();
start = i;
if (token.Length == 0)
{
return null;
}
return new GuanExpression(ReadLiteral(token, quote));
}
private static void Process(Stack<Operator> operators,
Stack<GuanExpression> tokens)
{
Operator op = operators.Pop();
GuanExpression result = new GuanExpression(op.Func);
if (tokens.Count < 2)
{
throw new ArgumentException("Expression not well formed");
}
GuanExpression token2 = tokens.Pop();
GuanExpression token1 = tokens.Pop();
if (token1 != null)
{
result.AddChild(token1);
}
if (token2 != null)
{
result.AddChild(token2);
}
tokens.Push(result);
}
private static GuanExpression PrivateBuild(string exp, IGuanExpressionContext expressionContext)
{
Stack<Operator> operators = new Stack<Operator>();
Stack<GuanExpression> tokens = new Stack<GuanExpression>();
operators.Push(s_sentinel);
// We consider the expression to be made of token and operators
// alternatively. For unary operator, we will consider there
// to be a null token between it and the other token. Similarly
// for operator that does not have any argument, we consider it
// to have two null tokens around it.
// "(" is a special case where we will remove the preceding null
// token immediately before we push it on operator stack.
// Also when ")" is processed, we look for a next operator instead
// of null token. It can be considered that the null token after
// ")" is consumed immediately.
int start = 0;
bool isToken = true;
// The expression is considered to end with a token (can be null)
// so the loop always exit when searching for an operator while
// reaching the end of the expression.
while (isToken || start < exp.Length)
{
if (isToken)
{
GuanExpression token = ReadToken(exp, expressionContext, ref start);
tokens.Push(token);
}
else
{
// Filter white space when matching operators.
while (start < exp.Length && char.IsWhiteSpace(exp[start]))
{
start++;
}
Operator op = ReadOperator(exp, ref start);
if (op == null)
{
string msg = string.Format(CultureInfo.InvariantCulture,
"Expression {0} not well formed. Unrecognized operator: {1}",
exp, exp.Substring(start));
throw new ArgumentException(msg);
}
if (op.Op == "(")
{
GuanExpression dummy = tokens.Pop();
ReleaseAssert.IsTrue(dummy == null,
"Expression: {0}", exp);
operators.Push(op);
}
else if (op.Op == ")")
{
while (operators.Peek().Op != "(")
{
Process(operators, tokens);
}
operators.Pop();
isToken = true;
}
else
{
// Process the higher priority operators on the stack.
while (operators.Peek().Level >= op.Level)
{
Process(operators, tokens);
}
operators.Push(op);
}
}
isToken = !isToken;
}
// Leave the sential
while (operators.Count > 1)
{
Process(operators, tokens);
}
ReleaseAssert.IsTrue(tokens.Count == 1,
"Expression: {0}", exp);
GuanExpression result = tokens.Pop();
result.Bind();
return result;
}
/// <summary>
/// Build a trace expression from a string.
/// </summary>
/// <param name="exp">The string expression.</param>
/// <returns>The trace expression built from the string.</returns>
public static GuanExpression Build(string exp, IGuanExpressionContext expressionContext = null)
{
if (string.IsNullOrEmpty(exp))
{
return null;
}
if (exp.StartsWith("{", StringComparison.Ordinal) && exp.EndsWith("}", StringComparison.Ordinal))
{
if (exp.StartsWith("{{", StringComparison.Ordinal) && exp.EndsWith("}}", StringComparison.Ordinal))
{
exp = "expression(\"" + s_evalProperty.Replace(exp.Substring(2, exp.Length - 4), EvalProperty) + "\")";
}
else
{
exp = "add(\"" + s_evalProperty.Replace(exp.Substring(1, exp.Length - 2), EvalProperty) + "\")";
}
}
try
{
return PrivateBuild(exp, expressionContext);
}
catch (Exception e)
{
throw new ArgumentException("Invalid expression: " + exp, e);
}
}
public static GuanExpression BuildGetExpression(string variable)
{
List<GuanExpression> child = new List<GuanExpression>(1)
{
new GuanExpression(variable)
};
return new GuanExpression(GetFunc.Singleton, child);
}
public static GuanFunc GetGuanFunc(string name)
{
GuanFunc result = GuanFunc.Get(name, null);
if (result == null)
{
Operator op = s_operators.Get(name);
if (op != null)
{
result = op.Func;
}
}
return result;
}
public bool IsSimpleGetExpression()
{
return (m_func == GetFunc.Singleton && m_children.Count == 1 && m_children[0].IsLiteral);
}
private static string EvalProperty(Match match)
{
string name = match.Groups[1].Value;
name = name.Substring(1, name.Length - 2);
if (name.Contains("(") && name.Contains(")"))
{
return "\"," + name + ",\"";
}
return "\",get(" + name + "),\"";
}
public List<string> GetContextVariables()
{
List<string> result = new List<string>();
GetContextVariable(result);
return result;
}
private void GetContextVariable(List<string> result)
{
for (int i = 0; i < m_children.Count; i++)
{
GuanExpression child = m_children[i];
if (m_func is GetFunc && i == 0 && child.m_func is Literal)
{
string variable = (string)child.Evaluate(null);
if (!result.Contains(variable))
{
result.Add(variable);
}
}
else
{
child.GetContextVariable(result);
}
}
}
public void ChangeContextVariable(string oldName, string newName)
{
foreach (GuanExpression child in m_children)
{
child.ChangeContextVariable(oldName, newName);
if (m_func is GetFunc && child.m_func is Literal && oldName == (string)child.Evaluate(null))
{
child.m_func = new Literal(newName);
}
}
}
public void ChangeContextVariables(Dictionary<string, string> namePairs)
{
foreach (GuanExpression child in m_children)
{
child.ChangeContextVariables(namePairs);
if (m_func is GetFunc && child.m_func is Literal)
{
string oldName = (string)child.Evaluate(null);
string newName;
if (namePairs.TryGetValue(oldName, out newName))
{
child.m_func = new Literal(newName);
}
}
}
}
public void ReplaceContextVariable(string oldVariable, object value)
{
foreach (GuanExpression child in m_children)
{
if (m_func is GetFunc && child.m_func is Literal && (string)child.Evaluate(null) == oldVariable)
{
m_func = new Literal(value);
m_children = s_nullChildren;
}
else
{
child.ReplaceContextVariable(oldVariable, value);
}
}
}
}
[Serializable]
internal class ExpressionException : GuanException
{
public ExpressionException(string message)
: base(message)
{
}
public ExpressionException(string message, Exception inner)
: base(message, inner)
{
}
}
}

1146
Guan/Common/GuanFunc.cs Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

478
Guan/Common/GuanObject.cs Normal file
Просмотреть файл

@ -0,0 +1,478 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Guan.Common
{
public interface IGuanObject
{
GuanObject ToGuanObject();
}
public interface IGuanObjectTransformer
{
List<GuanObject> Transform(GuanObject original);
void Apply(GuanObject original, GuanObject target);
}
class ListObjFunc : GuanFunc
{
public static ListObjFunc Singleton = new ListObjFunc();
private ListObjFunc() : base("ListObj")
{
}
public override object Invoke(IPropertyContext context, object[] args)
{
if (args.Length != 1 && args.Length != 2)
{
throw new ArgumentException("Need 1 or 2 arguments");
}
GuanObject result = new GuanObject();
IEnumerable arg1 = ConvertArg(args[0]);
if (arg1 == null)
{
throw new ArgumentException("Invalid argument");
}
if (args.Length == 1)
{
foreach (object obj in arg1)
{
result["#append"] = obj;
}
}
else
{
IEnumerable arg2 = ConvertArg(args[1]);
if (arg1 == null)
{
throw new ArgumentException("Invalid argument");
}
IEnumerator enumerator1 = arg1.GetEnumerator();
IEnumerator enumerator2 = arg2.GetEnumerator();
while (enumerator1.MoveNext() && enumerator2.MoveNext())
{
result[enumerator1.Current.ToString()] = enumerator2.Current;
}
}
return result;
}
private IEnumerable ConvertArg(object arg)
{
if (arg is string)
{
return null;
}
return arg as IEnumerable;
}
}
public class GuanObject : IPropertyContext, IWritablePropertyContext, IEnumerable
{
private object value_;
private GuanObject parent_;
private SortedDictionary<string, GuanObject> children_;
private Dictionary<string, string> aliases_;
private static readonly char[] PathDelimit = new char[] { '/' };
private static readonly List<string> EmptyList = new List<string>();
class EncodePathFunc : UnaryFunc
{
public static EncodePathFunc Singleton = new EncodePathFunc();
private EncodePathFunc() : base("EncodePath")
{
}
public override object UnaryInvoke(object arg)
{
string input = (string)arg;
return input.Replace("/", "%2F");
}
}
class DecodePathFunc : UnaryFunc
{
public static DecodePathFunc Singleton = new DecodePathFunc();
private DecodePathFunc() : base("DecodePath")
{
}
public override object UnaryInvoke(object arg)
{
string input = (string)arg;
return input.Replace("%2F", "/");
}
}
public GuanObject()
{
children_ = new SortedDictionary<string, GuanObject>();
}
public GuanObject(GuanObject other, GuanObject parent = null)
{
parent_ = parent;
aliases_ = other.aliases_;
children_ = new SortedDictionary<string, GuanObject>();
CopyFrom(other);
}
protected Dictionary<string, string> Aliases
{
get
{
return aliases_;
}
set
{
aliases_ = value;
}
}
public IEnumerator GetEnumerator()
{
return children_.GetEnumerator();
}
public SortedDictionary<string, GuanObject> Children
{
get
{
return children_;
}
}
public virtual void CopyFrom(GuanObject other)
{
value_ = other.value_;
CopyChildren(other);
}
public void CopyChildren(GuanObject other, Dictionary<string, string> children = null, bool overwrite = true)
{
foreach (KeyValuePair<string, GuanObject> entry in other.children_)
{
if (children == null)
{
if (overwrite || !children_.ContainsKey(entry.Key))
{
children_[entry.Key] = new GuanObject(entry.Value, this);
}
}
else
{
string newKey;
if (children.TryGetValue(entry.Key, out newKey))
{
children_.Add(newKey, new GuanObject(entry.Value, this));
}
}
}
}
public virtual object this[string name]
{
get
{
string path = name;
GuanObject obj = GetNode(ref path, false);
if (obj == null)
{
if (aliases_ != null && aliases_.TryGetValue(name, out path))
{
obj = GetNode(ref path, false);
}
if (obj == null)
{
return null;
}
}
return obj.GetValue(path);
}
set
{
SetValue(name, value, null);
}
}
public GuanObject GetObject(string name)
{
string path = name;
return GetNode(ref path, false);
}
public bool ContainsKey(string key)
{
GuanObject obj = GetNode(ref key, false);
return (obj != null);
}
public T GetValue<T>(string name)
{
object value = this[name];
return Utility.Convert<T>(value);
}
public virtual void SetValue(string name, object value, string operation)
{
if (operation == "delete")
{
Delete(name);
return;
}
string path = name;
GuanObject obj = GetNode(ref path, true);
GuanObject child = value as GuanObject;
if (operation == "Children")
{
if (child != null)
{
obj.CopyChildren(child);
}
}
else if (child == null)
{
obj.SetValue(value, operation);
}
else
{
foreach (var entry in obj.parent_.children_)
{
if (entry.Value == obj)
{
obj.parent_.children_[entry.Key] = child;
child.parent_ = obj.parent_;
return;
}
}
ReleaseAssert.Fail("Child not found");
}
}
public void AddKey(string name)
{
SetValue(name, null, null);
}
public bool Delete(string name)
{
string path = name;
GuanObject obj = GetNode(ref path, false);
if (obj == null)
{
return false;
}
if (path.Length != 0)
{
throw new ArgumentException("name");
}
return obj.parent_.children_.Remove(GetLeafName(name));
}
private string GetLeafName(string name)
{
int index = name.LastIndexOf('/');
if (index < 0)
{
return name;
}
return name.Substring(index + 1);
}
private object GetValue(string name)
{
if (name == "#Count")
{
return children_.Count;
}
if (value_ != null)
{
return value_;
}
return this;
}
public object GetValue()
{
return value_;
}
private void SetValue(object value, string operation)
{
if (operation == null)
{
value_ = value;
}
else if (value != null)
{
if (operation == "+")
{
Add(value);
}
else if (operation == "count")
{
if (value != null)
{
Count(value);
}
}
else
{
ReleaseAssert.IsTrue(operation == "Create", "Unknown operation: " + operation);
}
}
}
private void Count(object value)
{
string key = value.ToString();
GuanObject obj = GetNode(ref key, true);
obj.Add(1);
}
private GuanObject GetNode(ref string name, bool create)
{
if (string.IsNullOrEmpty(name))
{
return this;
}
GuanObject result = this;
string[] path = name.Split(PathDelimit, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < path.Length; i++)
{
if (path[i] == "#append")
{
path[i] = result.children_.Count.ToString();
}
if (path[i].StartsWith("#", StringComparison.InvariantCulture))
{
if (i < path.Length - 1)
{
throw new ArgumentException("Invalid name: " + name);
}
name = path[i];
return result;
}
GuanObject child;
if (!result.children_.TryGetValue(path[i], out child))
{
if (!create)
{
return null;
}
child = new GuanObject();
child.parent_ = result;
result.children_.Add(path[i], child);
}
result = child;
}
name = string.Empty;
return result;
}
private void Add(object value)
{
value_ = (Utility.Convert<long>(value_) + Utility.Convert<long>(value)).ToString();
}
public override string ToString()
{
StringBuilder result = new StringBuilder();
if (children_.Count > 0)
{
result.Append("{");
}
if (value_ != null)
{
IEnumerable valueCollection = value_ as IEnumerable;
if (valueCollection != null && value_.GetType() != typeof(string))
{
result.Append("(");
int i = 0;
foreach (var entry in valueCollection)
{
if (i > 0)
{
result.Append(",");
}
result.Append(entry.ToString());
i++;
}
result.Append(")");
}
else
{
result.Append(value_);
}
}
if (children_.Count > 0)
{
if (children_.Count > 12)
{
if (result.Length > 1)
{
result.Append(",");
}
result.AppendFormat("#Count:{0}", children_.Count);
}
else
{
foreach (var entry in children_)
{
if (!entry.Key.StartsWith("_"))
{
if (result.Length > 1)
{
result.Append(",");
}
result.AppendFormat("{0}:{1}", entry.Key, entry.Value);
}
}
}
if (result.Length > 1)
{
result.Append("}");
}
else
{
result.Length = 0;
}
}
return result.ToString();
}
}
}

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

@ -0,0 +1,432 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
namespace Guan.Common
{
[DataContract]
public class GuanObjectTransformSpecification
{
[DataContract]
public class Rule
{
[DataMember]
public string SourcePath;
[DataMember]
public string SourceExpression;
[DataMember]
public string Target;
[DataMember]
public bool IsRequired;
public Rule()
{
IsRequired = true;
}
}
class Tranformer : IGuanObjectTransformer
{
class StringWithVariable
{
private List<string> fragments_;
private List<string> variables_;
private bool literal_;
private StringWithVariable(List<string> fragments, List<string> variables)
{
fragments_ = fragments;
variables_ = variables;
literal_ = (fragments.Count == 2 && string.IsNullOrEmpty(fragments[0]) && string.IsNullOrEmpty(fragments[1]));
}
public string Evaluate(GuanObject obj)
{
string result = fragments_[0];
for (int i = 1; i < fragments_.Count; i++)
{
object value = obj["__variable/" + variables_[i - 1]];
result = result + (value != null ? value.ToString() : "") + fragments_[i];
}
return result;
}
public object Evaluate(GuanObject original, GuanObject obj, IGuanExpressionContext expressionContext)
{
if (literal_)
{
return obj["__variable/" + variables_[0]];
}
GuanExpression exp = GuanExpression.Build(Evaluate(obj), expressionContext);
return exp.Evaluate(original);
}
public static StringWithVariable Build(string original, List<string> variables)
{
List<string> fragments = null;
List<string> names = null;
int last = 0;
int start = 0;
while (start >= 0)
{
start = original.IndexOf('?', start);
if (start < 0)
{
if (fragments == null)
{
return null;
}
fragments.Add(original.Substring(last));
}
else
{
string tmp = original.Substring(start + 1);
bool found = false;
foreach (string variable in variables)
{
if (tmp.StartsWith(variable))
{
if (fragments == null)
{
fragments = new List<string>();
names = new List<string>();
}
fragments.Add(original.Substring(last, start - last));
names.Add(variable);
start = last = start + variable.Length + 1;
found = true;
break;
}
}
if (!found)
{
start = start + 1;
}
}
}
return new StringWithVariable(fragments, names);
}
}
class TransformRule
{
private Rule spec_;
private IGuanExpressionContext expressionContext_;
private string[] pathSegments_;
private GuanExpression expression_;
private GuanPredicate predicate_;
private StringWithVariable expressionToEvaluate_;
private StringWithVariable targetToEvaluate_;
private static readonly Regex VariablePattern = new Regex(@"^?\d+$", RegexOptions.Compiled);
public TransformRule(Rule spec, IGuanExpressionContext expressionContext, List<string> variables)
{
spec_ = spec;
expressionContext_ = expressionContext;
if (!string.IsNullOrEmpty(spec_.SourcePath))
{
pathSegments_ = spec_.SourcePath.Trim('/').Split('/');
bool found = false;
foreach (string segment in pathSegments_)
{
if (VariablePattern.IsMatch(segment))
{
string name = segment.Substring(1);
if (!variables.Contains(name))
{
variables.Add(name);
}
found = true;
}
}
if (!found)
{
pathSegments_ = null;
}
}
if (!string.IsNullOrEmpty(spec_.SourceExpression))
{
expressionToEvaluate_ = StringWithVariable.Build(spec_.SourceExpression, variables);
if (expressionToEvaluate_ == null)
{
if (string.IsNullOrEmpty(spec_.Target))
{
predicate_ = GuanPredicate.Build(spec_.SourceExpression, expressionContext_);
}
else
{
expression_ = GuanExpression.Build(spec_.SourceExpression, expressionContext_);
}
}
}
if (!string.IsNullOrEmpty(spec_.Target))
{
targetToEvaluate_ = StringWithVariable.Build(spec_.Target, variables);
}
}
public void Process(GuanObject original, GuanObject target)
{
object value;
if (string.IsNullOrEmpty(spec_.SourceExpression))
{
value = original[spec_.SourcePath];
}
else
{
value = expression_.Evaluate(original);
}
if (value != null)
{
target[spec_.Target] = value;
}
}
public void Process(GuanObject original, List<GuanObject> result)
{
List<string> path = null;
if (pathSegments_ != null)
{
path = EvaluateVariables(original, result);
}
for (int i = result.Count - 1; i >= 0; i--)
{
if (!string.IsNullOrEmpty(spec_.Target))
{
object value;
if (string.IsNullOrEmpty(spec_.SourceExpression))
{
value = original[path != null ? path[i] : spec_.SourcePath];
}
else
{
value = EvaluateExpression(original, result[i]);
}
if (value != null)
{
result[i][EvaluateTarget(original)] = value;
}
else if (spec_.IsRequired)
{
result.RemoveAt(i);
}
}
else if (!EvaluatePredicate(original, result[i]))
{
result.RemoveAt(i);
}
}
}
private object EvaluateExpression(GuanObject original, GuanObject obj)
{
GuanExpression expression = expression_;
if (expression == null)
{
return expressionToEvaluate_.Evaluate(original, obj, expressionContext_);
}
return expression.Evaluate(original);
}
private bool EvaluatePredicate(GuanObject original, GuanObject obj)
{
GuanPredicate predicate = predicate_;
if (predicate == null)
{
predicate = GuanPredicate.Build(expressionToEvaluate_.Evaluate(obj), expressionContext_);
}
return predicate.Match(original);
}
private string EvaluateTarget(GuanObject obj)
{
return (targetToEvaluate_ != null ? targetToEvaluate_.Evaluate(obj) : spec_.Target);
}
private List<string> EvaluateVariables(GuanObject original, List<GuanObject> result)
{
List<string> path = new List<string>(result.Count);
for (int i = 0; i < pathSegments_.Length; i++)
{
string varName = (pathSegments_[i].StartsWith("?") ? "__variable/" + pathSegments_[i].Substring(1) : null);
int count = result.Count;
for (int j = 0; j < count; j++)
{
string segment;
if (varName == null)
{
segment = pathSegments_[i];
}
else
{
segment = (string)result[j][varName];
if (segment == null)
{
GuanObject parent = original.GetObject(path[j]);
if (parent != null)
{
foreach (var child in parent.Children)
{
GuanObject obj = new GuanObject(result[j]);
obj[varName] = child.Key;
result.Add(obj);
path.Add(i == 0 ? child.Key : path[j] + "/" + child.Key);
if (pathSegments_[i].StartsWith("??"))
{
break;
}
}
}
result.RemoveAt(j);
path.RemoveAt(j);
j--;
count--;
continue;
}
}
if (i == 0)
{
path.Add(segment);
}
else
{
path[j] = path[j] + "/" + segment;
}
}
}
return path;
}
}
private List<TransformRule> rules_;
private bool isSimple_;
public Tranformer(GuanObjectTransformSpecification spec, IGuanExpressionContext expressionContext)
{
List<string> variables = new List<string>();
rules_ = new List<TransformRule>(spec.Rules.Count);
isSimple_ = true;
foreach (var rule in spec.Rules)
{
if (rule.Target == null)
{
isSimple_ = false;
}
rules_.Add(new TransformRule(rule, expressionContext, variables));
}
if (variables.Count > 0)
{
isSimple_ = false;
}
}
public List<GuanObject> Transform(GuanObject original)
{
List<GuanObject> result = new List<GuanObject>
{
new GuanObject()
};
foreach (var rule in rules_)
{
rule.Process(original, result);
}
foreach (GuanObject obj in result)
{
obj.Delete("__variable");
}
return result;
}
public void Apply(GuanObject original, GuanObject target)
{
if (!isSimple_)
{
throw new InvalidOperationException("Apply can only be called with simple transformer");
}
if (rules_.Count > 0)
{
foreach (var rule in rules_)
{
rule.Process(original, target);
}
}
else if (original != target)
{
target.CopyFrom(original);
}
}
}
[DataMember]
public List<Rule> Rules;
public GuanObjectTransformSpecification()
{
Rules = new List<Rule>();
}
public void AddPredicate(string predicate)
{
AddMapping(null, predicate, null);
}
public void AddMapping(string source)
{
AddMapping(source, source);
}
public void AddMapping(string source, string target)
{
AddMapping(source, null, target, true);
}
public void AddMapping(string sourcePath, string sourceExpression, string target, bool isRequired = true)
{
Rule rule = new Rule();
rule.SourcePath = sourcePath;
rule.SourceExpression = sourceExpression;
rule.Target = target;
rule.IsRequired = isRequired;
Rules.Add(rule);
}
public IGuanObjectTransformer CreateTransformer(IGuanExpressionContext expressionContext)
{
return new Tranformer(this, expressionContext);
}
}
}

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

@ -0,0 +1,107 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.
//
// @File: GuanPredicate.cs
//
// @Owner: xunlu
// @Test: xunlu
//
// Purpose:
// Predicate expression used for matching trace records.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Guan.Common
{
public class GuanPredicate
{
private GuanExpression m_exp;
public static readonly GuanPredicate MatchAll = new GuanPredicate(true);
public static readonly GuanPredicate MatchNothing = new GuanPredicate(false);
private GuanPredicate(bool matchAll)
{
m_exp = new GuanExpression(matchAll ? Literal.True : Literal.False);
}
public GuanPredicate(GuanExpression exp)
{
m_exp = exp;
}
public GuanPredicate Clone()
{
return new GuanPredicate(m_exp.Clone());
}
public GuanExpression Expression
{
get
{
return m_exp;
}
}
public bool Match(IPropertyContext context)
{
if (m_exp == null)
{
return true;
}
object result = m_exp.Evaluate(context);
if (result == null)
{
return false;
}
if (result is bool)
{
return (bool) result;
}
string s = result as string;
if (s != null)
{
return s.Length > 0;
}
return true;
}
public bool SafeMatch(IPropertyContext context)
{
try
{
return Match(context);
}
catch (ExpressionException)
{
return false;
}
}
public static GuanPredicate Build(string exp, IGuanExpressionContext expressionContext = null)
{
GuanExpression expression = GuanExpression.Build(exp, expressionContext);
GuanPredicate result = new GuanPredicate(expression);
if (expression.IsLiteral)
{
result = (result.Match(null) ? MatchAll : MatchNothing);
}
return result;
}
public override string ToString()
{
if (m_exp == null)
{
return "MatchAll";
}
return m_exp.ToString();
}
}
}

164
Guan/Common/GuanTime.cs Normal file
Просмотреть файл

@ -0,0 +1,164 @@
using System;
namespace Guan.Common
{
public class GuanTime : IPartialOrder
{
private DateTime time_;
private string clockId_;
private object context_;
private IComparable contextLocation_;
public static readonly GuanTime MinValue = new GuanTime(DateTime.MinValue);
public static readonly GuanTime MaxValue = new GuanTime(DateTime.MaxValue);
public GuanTime(DateTime time)
{
time_ = time;
}
public GuanTime(DateTime time, string clockId) : this(time, clockId, null, null)
{
}
public GuanTime(DateTime time, string clockId, object context, IComparable contextLocation)
{
time_ = time;
if (time != DateTime.MaxValue && time != DateTime.MinValue)
{
clockId_ = clockId;
context_ = context;
contextLocation_ = contextLocation;
}
}
public DateTime Time
{
get
{
return time_;
}
}
public string ClockId
{
get
{
return clockId_;
}
}
public object Context
{
get
{
return context_;
}
}
public bool CompareTo(GuanTime other, out int result)
{
if (context_ == null || other.context_ == null)
{
result = time_.CompareTo(other.time_);
if (clockId_ != other.clockId_ && !string.IsNullOrEmpty(clockId_) && !string.IsNullOrEmpty(other.clockId_))
{
return false;
}
if (result == 0)
{
if (context_ != null)
{
result = 1;
}
else if (other.context_ != null)
{
result = -1;
}
}
}
else
{
if (!object.Equals(context_, other.context_))
{
result = 0;
return false;
}
result = contextLocation_.CompareTo(other.contextLocation_);
}
return true;
}
public int CompareTo(DateTime other)
{
int result = time_.CompareTo(other);
if (result == 0 && context_ != null)
{
result = 1;
}
return result;
}
public bool CompareTo(object other, out int result)
{
GuanTime otherGuanTime = other as GuanTime;
if (otherGuanTime != null)
{
return CompareTo(otherGuanTime, out result);
}
if (other is DateTime)
{
result = CompareTo((DateTime)other);
return true;
}
result = 0;
return false;
}
public override string ToString()
{
return Utility.FormatTime(time_);
}
public static explicit operator GuanTime(DateTime value)
{
return new GuanTime(value);
}
public static explicit operator DateTime(GuanTime value)
{
return value.time_;
}
public static GuanTime Convert(object value)
{
if (value == null)
{
return null;
}
GuanTime result = value as GuanTime;
if (result != null)
{
return result;
}
DateTime time = (DateTime)value;
if (time == DateTime.MinValue)
{
return GuanTime.MinValue;
}
if (time == DateTime.MaxValue)
{
return GuanTime.MaxValue;
}
return new GuanTime(time);
}
}
}

227
Guan/Common/Heap.cs Normal file
Просмотреть файл

@ -0,0 +1,227 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.
//
// @File: MinHeap.cs
//
// @Owner: xunlu
// @Test: xunlu
//
// Purpose:
// Provide a min heap implementation.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace Guan.Common
{
/// <summary>
/// MinHeap implementation.
/// The operations is NOT thread safe and it is up to the caller
/// to synchronize access to the heap.
/// </summary>
/// <typeparam name="T">The type stored in the heap</typeparam>
public class MinHeap<T> : IEnumerable<T>
{
private List<T> m_data;
private IComparer<T> m_comparer;
/// <summary>
/// Default constructor
/// </summary>
public MinHeap()
{
m_data = new List<T>();
m_comparer = Comparer<T>.Default;
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="capacity">Capacity of the heap</param>
public MinHeap(int capacity)
: this(capacity, Comparer<T>.Default)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="capacity">Capacity of the heap</param>
/// <param name="comparer">Comparer for the heap.</param>
public MinHeap(int capacity, IComparer<T> comparer)
{
m_data = new List<T>(capacity);
m_comparer = comparer;
}
/// <summary>
/// Whether the heap is empty
/// </summary>
public bool IsEmpty
{
get
{
return (m_data.Count == 0);
}
}
/// <summary>
/// The number of elements in the heap
/// </summary>
public int Count
{
get
{
return m_data.Count;
}
}
/// <summary>
/// Capacity of the heap.
/// </summary>
public int Capacity
{
get
{
return m_data.Capacity;
}
}
private void Swap(int i, int j)
{
T tmp = m_data[i];
m_data[i] = m_data[j];
m_data[j] = tmp;
}
/// <summary>
/// The min item stored in the heap.
/// </summary>
public T Top
{
get
{
if (IsEmpty)
{
throw new InvalidOperationException("heap is empty");
}
return m_data[0];
}
}
/// <summary>
/// Add an item to the heap.
/// </summary>
/// <param name="item">The item to be added</param>
public void Add(T item)
{
int index = m_data.Count;
int parentIndex;
m_data.Add(item);
while ((index > 0) &&
(m_comparer.Compare(item, m_data[(parentIndex = (index - 1) / 2)]) < 0))
{
Swap(index, parentIndex);
index = parentIndex;
}
}
/// <summary>
/// Extract the min item from the heap.
/// </summary>
/// <returns>The min item in the heap.</returns>
public T Extract()
{
if (IsEmpty)
{
throw new InvalidOperationException("heap is empty");
}
T result = m_data[0];
T item = m_data[0] = m_data[m_data.Count - 1];
m_data.RemoveAt(m_data.Count - 1);
int index = 0;
while (true)
{
int minIndex;
int leftIndex = (index << 1) + 1;
if ((leftIndex < m_data.Count) &&
(m_comparer.Compare(item, m_data[leftIndex]) > 0))
{
minIndex = leftIndex;
}
else
{
minIndex = index;
}
int rightIndex = leftIndex + 1;
if ((rightIndex < m_data.Count) &&
(m_comparer.Compare(m_data[minIndex], m_data[rightIndex]) > 0))
{
minIndex = rightIndex;
}
if (minIndex == index)
{
break;
}
Swap(index, minIndex);
index = minIndex;
}
return result;
}
/// <summary>
/// Clear all items in the heap.
/// </summary>
public void Clear()
{
m_data.Clear();
}
/// <summary>
/// Get an enumerator for all the items currently in heap.
/// </summary>
/// <returns>The enumerator for the heap.</returns>
public IEnumerator<T> GetEnumerator()
{
return m_data.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return m_data.GetEnumerator();
}
/// <summary>
/// String representation of the heap.
/// </summary>
/// <returns>String representation of the heap.</returns>
public override string ToString()
{
StringBuilder result = new StringBuilder(1024);
foreach (T item in m_data)
{
result.AppendFormat("{0} ", item);
}
if (result.Length > 0)
{
result.Length--;
}
return result.ToString();
}
}
}

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

@ -0,0 +1,20 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.
//
// @File: IConfigurable.cs
//
// @Owner: xunlu
// @Test: xunlu
//
// Purpose:
// Interface that allows object to be dynamically configured.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Guan.Common
{
public interface IConfigurable
{
void SetConfig(string category, string option);
}
}

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

@ -0,0 +1,7 @@
namespace Guan.Common
{
public interface IPartialOrder
{
bool CompareTo(object other, out int result);
}
}

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

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.
//
// @File: IPropertyContext.cs
//
// @Owner: xunlu
// @Test: xunlu
//
// Purpose:
// Define the interface for retrieving named property.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Guan.Common
{
/// <summary>
/// Interface implemented by classes that can expose named properties.
/// The implementing class is free to define any property it wants
/// to expose. In the advanced scenario, the object can even define
/// an infinite set of properties.
/// </summary>
public interface IPropertyContext
{
/// <summary>
/// Property with a string name.
/// </summary>
/// <param name="name">Property name.</param>
/// <returns>Property value, null if not found or the value
/// is explicitly set to null.</returns>
object this[string name]
{
get;
}
}
public interface IWritablePropertyContext
{
/// <summary>
/// Property with a string name.
/// </summary>
/// <param name="name">Property name.</param>
/// <returns>Property value, null if not found or the value
/// is explicitly set to null.</returns>
object this[string name]
{
set;
}
}
}

151
Guan/Common/MathsFunc.cs Normal file
Просмотреть файл

@ -0,0 +1,151 @@
using System;
namespace Guan.Common
{
internal class MathsFunc : StandaloneFunc
{
private char op_;
public static MathsFunc Minus = new MathsFunc("minus", '-');
public static MathsFunc Multiply = new MathsFunc("multiply", '*');
public static MathsFunc Divide = new MathsFunc("divide", '/');
public static MathsFunc Mod = new MathsFunc("mod", '%');
public MathsFunc(string name, char op)
: base(name)
{
this.op_ = op;
}
public override object Invoke(object[] args)
{
if (args.Length == 1 && this == Minus)
{
return UnaryMinus(args[0]);
}
if (args.Length != 2)
{
throw new ArgumentException("Invalid number of args");
}
object result = Calculate(args[0], args[1]);
ReleaseAssert.IsTrue(result != null, "Incompatible type for arithemetic operation: {0}, {1}", args[0], args[1]);
return result;
}
private object UnaryMinus(object arg)
{
Type type = arg.GetType();
if (type == typeof(int))
{
int value = (int)arg;
return -value;
}
if (type == typeof(long))
{
long value = (long)arg;
return -value;
}
if (type == typeof(ulong))
{
ulong value = (ulong)arg;
return -((long)value);
}
if (type == typeof(TimeSpan))
{
TimeSpan value = (TimeSpan)arg;
return -value;
}
throw new GuanException("Invalid type for unary minus function: ", type);
}
private object Calculate(object arg1, object arg2)
{
if (arg1 == null || arg2 == null)
{
return null;
}
Type type = arg1.GetType();
if (type != arg2.GetType())
{
return null;
}
if (type == typeof(long))
{
long v1 = (long)arg1;
long v2 = (long)arg2;
switch (op_)
{
case '+':
return v1 + v2;
case '-':
return v1 - v2;
case '*':
return v1 * v2;
case '/':
return v1 / v2;
case '%':
return v1 % v2;
}
}
else if (type == typeof(ulong))
{
ulong v1 = (ulong)arg1;
ulong v2 = (ulong)arg2;
switch (op_)
{
case '+':
return v1 + v2;
case '-':
return v1 - v2;
case '*':
return v1 * v2;
case '/':
return v1 / v2;
case '%':
return v1 % v2;
}
}
else if (type == typeof(int))
{
int v1 = (int)arg1;
int v2 = (int)arg2;
switch (op_)
{
case '+':
return v1 + v2;
case '-':
return v1 - v2;
case '*':
return v1 * v2;
case '/':
return v1 / v2;
case '%':
return v1 % v2;
}
}
else if (type == typeof(double))
{
double v1 = (double)arg1;
double v2 = (double)arg2;
switch (op_)
{
case '+':
return v1 + v2;
case '-':
return v1 - v2;
case '*':
return v1 * v2;
case '/':
return v1 / v2;
}
}
return null;
}
}
}

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

@ -0,0 +1,121 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.
//
// @File: PropertyMatchFunc.cs
//
// @Owner: xunlu
// @Test: xunlu
//
// Purpose:
// Function to match/compare trace property.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
namespace Guan.Common
{
public class PropertyMatchFunc : GuanFunc
{
class BoundPropertyMatch : GuanFunc
{
private string m_propertyName;
private Regex m_pattern;
private bool m_not;
public BoundPropertyMatch(string name, string propertyName, string pattern, bool not)
: base(name)
{
m_propertyName = propertyName;
m_pattern = new Regex(pattern, RegexOptions.Compiled);
m_not = not;
}
public override object Invoke(IPropertyContext context, object[] args)
{
object propertyValue = context[m_propertyName];
string property = (propertyValue != null ? propertyValue.ToString() : string.Empty);
return (m_pattern.IsMatch(property) != m_not);
}
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "{0}({1},{2})", Name, m_propertyName, m_pattern);
}
}
public static readonly PropertyMatchFunc Equal = new PropertyMatchFunc("propequal", true, false);
public static readonly PropertyMatchFunc OptionalEqual = new PropertyMatchFunc("propeq", true, false, true);
public static readonly PropertyMatchFunc Match = new PropertyMatchFunc("propmatch", false, false);
public static readonly PropertyMatchFunc NotEqual = new PropertyMatchFunc("propnotequal", true, true);
public static readonly PropertyMatchFunc NotMatch = new PropertyMatchFunc("propnotmatch", false, true);
private bool m_exactMatch;
private bool m_not;
private bool m_optional;
private PropertyMatchFunc(string name, bool exactMatch, bool not, bool optional = false)
: base(name)
{
m_exactMatch = exactMatch;
m_not = not;
m_optional = optional;
}
public override object Invoke(IPropertyContext context, object[] args)
{
int argCount = args.Length;
if (argCount == 0)
{
throw new ArgumentException("Invalid number of arguments for Match()");
}
string propertyName = (string) args[0];
string pattern = (argCount > 1 && args[1] != null ? args[1].ToString() : string.Empty);
if (pattern == null && m_optional)
{
return true;
}
object propertyValue = GetFunc.Invoke(context, propertyName);
string property = (propertyValue != null ? propertyValue.ToString() : string.Empty);
bool result;
if (m_exactMatch)
{
result = (property == pattern);
}
else
{
result = new Regex(pattern).IsMatch(property);
}
return (result != m_not);
}
internal override GuanFunc Bind(List<GuanExpression> args)
{
string pattern;
string propertyName;
int argCount = args.Count;
if (!m_exactMatch && args[argCount - 1].GetLiteral(out pattern))
{
if (!args[0].GetLiteral(out propertyName))
{
return this;
}
args.Clear();
return new BoundPropertyMatch(ToString(), propertyName, pattern, m_not);
}
return this;
}
}
}

240
Guan/Common/Range.cs Normal file
Просмотреть файл

@ -0,0 +1,240 @@
using System;
namespace Guan.Common
{
public class Range<T> where T : IComparable
{
private T begin_;
private T end_;
private int beginComparand_;
private int endComparand_;
public Range(T begin, T end, bool beginInclusive = true, bool endInclusive = true, bool skipCheck = false)
{
if (!skipCheck && begin.CompareTo(end) > 0)
{
throw new ArgumentException(Utility.FormatString("Invalid range: {0}-{1}", begin, end));
}
begin_ = begin;
end_ = end;
beginComparand_ = (beginInclusive ? 0 : -1);
endComparand_ = (endInclusive ? 0 : 1);
}
public Range(Range<T> other)
: this(other.Begin, other.End, other.IsBeginInclusive, other.IsEndInclusive)
{
}
public T Begin
{
get
{
return begin_;
}
protected set
{
begin_ = value;
}
}
public T End
{
get
{
return end_;
}
protected set
{
end_ = value;
}
}
public bool IsBeginInclusive
{
get
{
return beginComparand_ == 0;
}
}
public bool IsEndInclusive
{
get
{
return endComparand_ == 0;
}
}
public bool Contains(T value)
{
return (begin_.CompareTo(value) <= beginComparand_ && end_.CompareTo(value) >= endComparand_);
}
public bool WeakContains(T value)
{
return (begin_.CompareTo(value) <= 0 && end_.CompareTo(value) >= 0);
}
public bool StrongContains(T value)
{
return (begin_.CompareTo(value) < 0 && end_.CompareTo(value) > 0);
}
public bool Contains(Range<T> other)
{
int result1 = begin_.CompareTo(other.begin_);
if ((result1 > 0) || (result1 == 0 && !IsBeginInclusive && other.IsBeginInclusive))
{
return false;
}
int result2 = end_.CompareTo(other.end_);
return (result2 > 0) || (result2 == 0 && (IsEndInclusive || !other.IsEndInclusive));
}
public bool WeakContains(Range<T> other)
{
return (begin_.CompareTo(other.begin_) <= 0 && end_.CompareTo(other.end_) >= 0);
}
public bool Overlaps(Range<T> other)
{
int result = end_.CompareTo(other.begin_);
if ((result < 0) || (result == 0 && (endComparand_ != 0 || other.beginComparand_ != 0)))
{
return false;
}
result = other.end_.CompareTo(begin_);
return (result > 0 || (result == 0 && other.endComparand_ == 0 && beginComparand_ == 0));
}
public bool Merge(Range<T> other)
{
int result1 = begin_.CompareTo(other.begin_);
if (result1 > 0)
{
int result2 = begin_.CompareTo(other.end_);
if ((result2 > 0) || (result2 == 0 && !IsBeginInclusive && !other.IsEndInclusive))
{
return false;
}
begin_ = other.begin_;
beginComparand_ = other.beginComparand_;
}
else if (result1 == 0 && !IsBeginInclusive)
{
beginComparand_ = other.beginComparand_;
}
result1 = end_.CompareTo(other.end_);
if (result1 < 0)
{
int result2 = end_.CompareTo(other.begin_);
if ((result2 < 0) || (result2 == 0 && !IsEndInclusive && !other.IsBeginInclusive))
{
return false;
}
end_ = other.end_;
endComparand_ = other.endComparand_;
}
else if (result1 == 0 && !IsEndInclusive)
{
endComparand_ = other.endComparand_;
}
return true;
}
protected bool Intersect(Range<T> other)
{
int result1 = begin_.CompareTo(other.begin_);
if (result1 < 0)
{
int result2 = end_.CompareTo(other.begin_);
if ((result2 < 0) || (result2 == 0 && !IsBeginInclusive && !other.IsEndInclusive))
{
return false;
}
begin_ = other.begin_;
beginComparand_ = other.beginComparand_;
}
else if (result1 == 0 && IsBeginInclusive)
{
beginComparand_ = other.beginComparand_;
}
result1 = end_.CompareTo(other.end_);
if (result1 > 0)
{
int result2 = begin_.CompareTo(other.end_);
if ((result2 > 0) || (result2 == 0 && !IsEndInclusive && !other.IsBeginInclusive))
{
return false;
}
end_ = other.end_;
endComparand_ = other.endComparand_;
}
else if (result1 == 0 && IsEndInclusive)
{
endComparand_ = other.endComparand_;
}
return true;
}
public static bool TryParse(string value, out Range<T> result)
{
int index = value.IndexOf('-');
if (index < 0)
{
T start;
if (Utility.TryParse<T>(value, out start))
{
result = new Range<T>(start, start);
return true;
}
}
else
{
T start, end;
if (Utility.TryParse(value.Substring(0, index - 1).Trim(), Utility.MinValue<T>(), out start) &&
Utility.TryParse(value.Substring(index + 1), Utility.MaxValue<T>(), out end))
{
result = new Range<T>(start, end);
return true;
}
}
result = null;
return false;
}
public override bool Equals(object obj)
{
Range<T> other = obj as Range<T>;
if (other == null)
{
return false;
}
return (begin_.CompareTo(other.begin_) == 0 && end_.CompareTo(other.end_) == 0 && beginComparand_ == other.beginComparand_ && endComparand_ == other.endComparand_);
}
public override int GetHashCode()
{
return begin_.GetHashCode();
}
public override string ToString()
{
return string.Format("{0}{1}-{2}{3}", IsBeginInclusive ? '[' : '(', begin_, end_, IsEndInclusive ? ']' : ')');
}
}
}

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

@ -0,0 +1,253 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.
//
// @File: ReleaseAssert.cs
//
// @Owner: xunlu
// @Test: xunlu
//
// Purpose:
// Helper class for providing assert behavior in production environment.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
using System;
using System.Globalization;
namespace Guan.Common
{
/// <summary>
/// Assertion class that will dump debug information and terminate the
/// process when assert fails.
/// Should be used to verify invariances.
/// </summary>
public static class ReleaseAssert
{
/// <summary>
/// Terminate the process after logging debug information.
/// </summary>
/// <param name="format">The format string for logging.</param>
/// <param name="args">The arguments for logging. If the argument
/// object supports IDumpable, this interface will be used to dump
/// the object. Otherwise ToString will be used.</param>
public static void Fail(string format, params object[] args)
{
string message;
if ((args != null) && (args.Length > 0))
{
message = string.Format(CultureInfo.InvariantCulture, format, args);
}
else
{
message = format;
}
EventLog.WriteError("Assert", "Assert Failed: {0}\nStack:{1}", message, Environment.StackTrace);
throw new SystemException("Assert Failed");
}
/// <summary>
/// Assert that the condition is true. Otherwise terminate the process
/// after logging debug information.
/// </summary>
/// <param name="condition">The condition to assert.</param>
/// <param name="format">The format string for logging.</param>
/// <param name="args">The arguments for logging. If the argument
/// object supports IDumpable, this interface will be used to dump
/// the object. Otherwise ToString will be used.</param>
public static void IsTrue(bool condition, string format, object[] args)
{
if (!condition)
{
Fail(format, args);
}
return;
}
/// <summary>
/// Assert that the condition is true. Otherwise terminate the process
/// after logging debug information.
/// </summary>
/// <param name="condition">The condition to assert.</param>
/// <param name="format">The format string for logging.</param>
public static void IsTrue(bool condition, string format)
{
if (!condition)
{
Fail(format);
}
return;
}
/// <summary>
/// Assert that the condition is true. Otherwise terminate the process
/// after logging debug information.
/// </summary>
/// <param name="condition">The condition to assert.</param>
/// <param name="format">The format string for logging.</param>
/// <param name="t1">The argument for logging.</param>
public static void IsTrue<T1>(bool condition, string format, T1 t1)
{
if (!condition)
{
Fail(format, t1);
}
return;
}
/// <summary>
/// Assert that the condition is true. Otherwise terminate the process
/// after logging debug information.
/// </summary>
/// <param name="condition">The condition to assert.</param>
/// <param name="format">The format string for logging.</param>
/// <param name="t1">The argument for logging.</param>
/// <param name="t2">The argument for logging.</param>
public static void IsTrue<T1, T2>(bool condition, string format, T1 t1, T2 t2)
{
if (!condition)
{
Fail(format, t1, t2);
}
return;
}
/// <summary>
/// Assert that the condition is true. Otherwise terminate the process
/// after logging debug information.
/// </summary>
/// <param name="condition">The condition to assert.</param>
/// <param name="format">The format string for logging.</param>
/// <param name="t1">The argument for logging.</param>
/// <param name="t2">The argument for logging.</param>
/// <param name="t3">The argument for logging.</param>
public static void IsTrue<T1, T2, T3>(bool condition, string format, T1 t1, T2 t2, T3 t3)
{
if (!condition)
{
Fail(format, t1, t2, t3);
}
return;
}
/// <summary>
/// Assert that the condition is true. Otherwise terminate the process
/// after logging debug information.
/// </summary>
/// <param name="condition">The condition to assert.</param>
/// <param name="format">The format string for logging.</param>
/// <param name="t1">The argument for logging.</param>
/// <param name="t2">The argument for logging.</param>
/// <param name="t3">The argument for logging.</param>
/// <param name="t4">The argument for logging.</param>
public static void IsTrue<T1, T2, T3, T4>(bool condition, string format, T1 t1, T2 t2, T3 t3, T4 t4)
{
if (!condition)
{
Fail(format, t1, t2, t3, t4);
}
return;
}
/// <summary>
/// Assert that the condition is true. Otherwise terminate the process
/// after logging debug information.
/// </summary>
/// <param name="condition">The condition to assert.</param>
/// <param name="format">The format string for logging.</param>
/// <param name="t1">The argument for logging.</param>
/// <param name="t2">The argument for logging.</param>
/// <param name="t3">The argument for logging.</param>
/// <param name="t4">The argument for logging.</param>
public static void IsTrue<T1, T2, T3, T4, T5>(bool condition, string format, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5)
{
if (!condition)
{
Fail(format, t1, t2, t3, t4, t5);
}
return;
}
/// <summary>
/// Assert that the condition is true. Otherwise terminate the process
/// after logging debug information.
/// </summary>
/// <param name="condition">The condition to assert.</param>
/// <param name="format">The format string for logging.</param>
/// <param name="t1">The argument for logging.</param>
/// <param name="t2">The argument for logging.</param>
/// <param name="t3">The argument for logging.</param>
/// <param name="t4">The argument for logging.</param>
public static void IsTrue<T1, T2, T3, T4, T5, T6>(bool condition, string format, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6)
{
if (!condition)
{
Fail(format, t1, t2, t3, t4, t5, t6);
}
return;
}
/// <summary>
/// Assert that the condition is true. Otherwise terminate the process
/// after logging debug information.
/// </summary>
/// <param name="condition">The condition to assert.</param>
/// <param name="format">The format string for logging.</param>
/// <param name="t1">The argument for logging.</param>
/// <param name="t2">The argument for logging.</param>
/// <param name="t3">The argument for logging.</param>
/// <param name="t4">The argument for logging.</param>
public static void IsTrue<T1, T2, T3, T4, T5, T6, T7>(bool condition, string format, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7)
{
if (!condition)
{
Fail(format, t1, t2, t3, t4, t5, t6, t7);
}
return;
}
/// <summary>
/// Assert that the condition is true. Otherwise terminate the process
/// after logging debug information.
/// </summary>
/// <param name="condition">The condition to assert.</param>
/// <param name="format">The format string for logging.</param>
/// <param name="t1">The argument for logging.</param>
/// <param name="t2">The argument for logging.</param>
/// <param name="t3">The argument for logging.</param>
/// <param name="t4">The argument for logging.</param>
public static void IsTrue<T1, T2, T3, T4, T5, T6, T7, T8>(bool condition, string format, T1 t1, T2 t2, T3 t3, T4 t4, T5 t5, T6 t6, T7 t7, T8 t8)
{
if (!condition)
{
Fail(format, t1, t2, t3, t4, t5, t6, t7, t8);
}
return;
}
/// <summary>
/// Assert that the condition is true. Otherwise terminate the process
/// after logging debug information.
/// </summary>
/// <param name="condition">The condition to assert.</param>
public static void IsTrue(bool condition)
{
if (!condition)
{
Fail(string.Empty, null);
}
return;
}
}
}

25
Guan/Common/SplitFunc.cs Normal file
Просмотреть файл

@ -0,0 +1,25 @@
namespace Guan.Common
{
internal class SplitFunc : BinaryFunc
{
public static readonly SplitFunc Singleton = new SplitFunc();
private SplitFunc()
: base("Split")
{
}
protected override object InvokeBinary(object arg1, object arg2)
{
string input = (string) arg1;
string delimiter = (string) arg2;
char[] delimiters = new char[delimiter.Length];
for (int i = 0; i < delimiter.Length; i++)
{
delimiters[i] = delimiter[i];
}
return input.Split(delimiters);
}
}
}

105
Guan/Common/StringFunc.cs Normal file
Просмотреть файл

@ -0,0 +1,105 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.
//
// @File: StrFunc.cs
//
// @Owner: xunlu
// @Test: xunlu
//
// Purpose:
// Function to convert object to literal string with quotation marks.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace Guan.Common
{
internal class StringFunc : UnaryFunc
{
public static readonly StringFunc Singleton = new StringFunc();
private StringFunc()
: base("string")
{
}
public override object UnaryInvoke(object arg)
{
return (arg != null ? arg.ToString() : string.Empty);
}
}
internal class StrFunc : UnaryFunc
{
public static readonly StrFunc Singleton = new StrFunc();
private StrFunc()
: base("str")
{
}
public override object UnaryInvoke(object arg)
{
string result = (arg != null ? arg.ToString() : string.Empty);
return "\"" + result + "\"";
}
}
internal class TrimFunc : UnaryFunc
{
public static readonly TrimFunc Singleton = new TrimFunc();
private TrimFunc()
: base("Trim")
{
}
public override object UnaryInvoke(object arg)
{
string result = (arg != null ? arg.ToString() : string.Empty);
return result.Trim();
}
}
internal class SubStrFunc : StandaloneFunc
{
public static readonly SubStrFunc Singleton = new SubStrFunc();
private SubStrFunc()
: base("substr")
{
}
public override object Invoke(object[] args)
{
if (args.Length == 0)
{
return string.Empty;
}
string result = args[0] as string;
if (result == null)
{
return string.Empty;
}
int start = 0;
int end = result.Length;
if (args.Length > 1 && args[1] != null)
{
start = result.IndexOf((string)args[1]) + 1;
}
if (args.Length > 2)
{
end = result.IndexOf((string)args[2]);
if (end < 0)
{
end = result.Length;
}
}
return result.Substring(start, end - start);
}
}
}

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

@ -0,0 +1,124 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Microsoft Corporation.
//
// @File: TextConfigFile.cs
//
// @Owner: xunlu
// @Test: xunlu
//
// Purpose:
// Base class to construct objects from configuration information in
// text file.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.IO;
namespace Guan.Common
{
public abstract class TextConfigFile<T>
where T : IConfigurable
{
private string m_path;
private Dictionary<string, T> m_entries;
protected TextConfigFile()
{
m_entries = new Dictionary<string, T>();
}
protected abstract T CreateEntry(string name, string value);
public Dictionary<string, T> Entries
{
get
{
return m_entries;
}
}
protected string ConfigFilePath
{
get
{
return m_path;
}
}
public bool Load(string fileName)
{
m_path = Utility.GetFilePath(fileName);
if (!File.Exists(m_path))
{
return false;
}
using (StreamReader reader = new StreamReader(m_path))
{
string line;
while ((line = reader.ReadLine()) != null)
{
line = line.Trim();
if (line.Length > 0)
{
CreateEntry(line, reader);
}
}
}
return true;
}
private void CreateEntry(string line, StreamReader reader)
{
string name;
string value;
int index = line.IndexOf(':');
if (index < 0)
{
name = line;
value = string.Empty;
}
else
{
name = line.Substring(0, index).TrimEnd();
value = line.Substring(index + 1).TrimStart();
}
T entry = CreateEntry(name, value);
if (entry != null)
{
m_entries[name] = entry;
}
string category = null;
while ((line = reader.ReadLine()) != null)
{
line = line.Trim();
if (line.Length == 0)
{
return;
}
if (entry != null && !line.StartsWith("#", StringComparison.Ordinal))
{
if (line.StartsWith("[", StringComparison.Ordinal) && !line.StartsWith("[[", StringComparison.Ordinal))
{
index = line.IndexOf(']');
if (index > 0)
{
category = line.Substring(1, index - 1);
line = line.Substring(index + 1).TrimStart();
}
}
entry.SetConfig(category, line);
}
}
}
}
}

301
Guan/Common/TimeRange.cs Normal file
Просмотреть файл

@ -0,0 +1,301 @@
using System;
using System.Collections.Generic;
namespace Guan.Common
{
public class TimeRange : Range<DateTime>, IComparable<TimeRange>
{
public static readonly TimeRange Full = new TimeRange(DateTime.MinValue, DateTime.MaxValue);
public static readonly TimeRange Empty = new TimeRange(DateTime.MinValue, DateTime.MinValue);
public TimeRange(DateTime begin, DateTime end, bool skipCheck = false)
: base(begin, end, false, true, skipCheck)
{
}
public TimeRange(TimeRange other)
: base(other)
{
}
public TimeSpan Duration
{
get
{
return Utility.SafeSubtract(End, Begin);
}
}
public TimeRange Extend(TimeSpan duration)
{
return new TimeRange(Utility.SafeSubtract(Begin, duration), Utility.SafeAdd(End, duration));
}
public DateTime MiddlePoint
{
get
{
return new DateTime((Begin.Ticks + End.Ticks) / 2);
}
}
public bool Merge(TimeRange other, TimeSpan gap)
{
if (Begin > other.Begin)
{
if (Begin > Utility.SafeAdd(other.End, gap))
{
return false;
}
Begin = other.Begin;
}
if (End < other.End)
{
if (Utility.SafeAdd(End, gap) < other.Begin)
{
return false;
}
End = other.End;
}
return true;
}
public void ForceMerge(TimeRange other)
{
if (Begin > other.Begin)
{
Begin = other.Begin;
}
if (End < other.End)
{
End = other.End;
}
}
public bool Subtract(TimeRange other, List<TimeRange> result)
{
if (other.Begin >= End || other.End <= Begin)
{
return false;
}
if (other.Begin > Begin)
{
result.Add(new TimeRange(Begin, other.Begin));
}
if (other.End < End)
{
result.Add(new TimeRange(other.End, End));
}
return true;
}
public override string ToString()
{
return Utility.FormatString("{0}, {1}", Utility.FormatTimeWithTicks(Begin), Utility.FormatTimeWithTicks(End));
}
public int CompareTo(TimeRange other)
{
if (Begin < other.Begin)
{
return CompareWithEarlierBeginTime(other);
}
else if (Begin > other.Begin)
{
return -other.CompareWithEarlierBeginTime(this);
}
else
{
return End.CompareTo(other.End);
}
}
private int CompareWithEarlierBeginTime(TimeRange other)
{
if (End <= other.End)
{
return -1;
}
TimeSpan dist1 = other.Begin - Begin;
TimeSpan dist2 = End - other.End;
return dist2.CompareTo(dist1);
}
public static TimeRange Intersect(TimeRange range1, TimeRange range2)
{
TimeRange result = new TimeRange(range1);
if (range1 == TimeRange.Empty || range2 == TimeRange.Empty || !result.Intersect(range2))
{
return null;
}
return result;
}
}
public class TimeRangeWithClock : TimeRange
{
private string beginClockId_;
private string endClockId_;
private object beginContext_;
private object endContext_;
public TimeRangeWithClock(DateTime begin, DateTime end, string beginClockId, string endClockId)
: base(begin, end, beginClockId != endClockId)
{
beginClockId_ = beginClockId;
endClockId_ = endClockId;
}
public TimeRangeWithClock(DateTime begin, DateTime end, string clockId)
: this(begin, end, clockId, clockId)
{
}
public TimeRangeWithClock(TimeRange range, string clockId)
: this(range.Begin, range.End, clockId, clockId)
{
}
public TimeRangeWithClock(TimeRangeWithClock other)
: base(other.Begin, other.End, other.beginClockId_ != other.endClockId_)
{
beginClockId_ = other.beginClockId_;
endClockId_ = other.endClockId_;
}
public string BeginClockId
{
get
{
return beginClockId_;
}
}
public string EndClockId
{
get
{
return endClockId_;
}
}
public object BeginContext
{
get
{
return beginContext_;
}
set
{
beginContext_ = value;
}
}
public object EndContext
{
get
{
return endContext_;
}
set
{
endContext_ = value;
}
}
public bool SameClock
{
get
{
return beginClockId_ == endClockId_;
}
}
}
internal class TimeRangeFunc : StandaloneFunc
{
public static TimeRangeFunc Singleton = new TimeRangeFunc();
private TimeRangeFunc() : base("TimeRange")
{
}
public override object Invoke(object[] args)
{
if (args == null || args.Length < 1)
{
throw new ArgumentException("args");
}
TimeRange range = args[0] as TimeRange;
if (range != null)
{
DateTime begin = range.Begin;
DateTime end = range.End;
if (args.Length > 1)
{
begin = GetTime(range, begin, args[1]);
}
if (args.Length > 2)
{
end = GetTime(range, end, args[2]);
}
return new TimeRange(begin, end);
}
else
{
if (args.Length != 2)
{
throw new ArgumentException("Two arguments are needed");
}
return new TimeRange((DateTime) args[0], (DateTime) args[1]);
}
}
private DateTime GetTime(TimeRange range, DateTime original, object arg)
{
double delta;
string stringArg = arg as string;
if (stringArg != null && stringArg.Length > 1 && stringArg[1] == ':')
{
if (stringArg[0] == '0')
{
original = range.Begin;
}
else if (stringArg[0] == '1')
{
original = range.End;
}
else
{
throw new ArgumentException("Invalid arg: " + arg);
}
stringArg = stringArg.Substring(2);
delta = double.Parse(stringArg);
}
else
{
delta = Utility.Convert<double>(arg);
}
return original + TimeSpan.FromSeconds(delta);
}
}
}

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

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
namespace Guan.Common
{
public class TimeRangeList : List<TimeRange>
{
public void AddRange(TimeRange range)
{
int index = FindRangeContainsOrAfter(range.Begin);
if (index == Count || this[index].Begin > range.End)
{
Insert(index, range);
}
else
{
this[index].Merge(range);
int i;
for (i = index + 1; i < Count && this[i].Begin <= this[index].End; i++)
{
this[index].Merge(this[i]);
}
if (i > index + 1)
{
RemoveRange(index + 1, i - index - 1);
}
}
}
public TimeRange GetFirstMissingRange(TimeRange range, TimeSpan minDuration)
{
DateTime begin = range.Begin;
int index = FindRangeContainsOrAfter(range.Begin);
while (index < Count && this[index].Begin < range.End)
{
TimeSpan duration = this[index].Begin - begin;
if (duration >= minDuration)
{
return new TimeRange(begin, this[index].Begin);
}
begin = this[index].End;
index++;
}
if (begin > range.End)
{
return null;
}
return new TimeRange(begin, range.End);
}
private int FindRangeContainsOrAfter(DateTime time)
{
int startIndex = 0;
int endIndex = Count;
while (startIndex < endIndex)
{
int mid = (startIndex + endIndex) / 2;
if (this[mid].End < time)
{
startIndex = mid + 1;
}
else
{
endIndex = mid;
}
}
return startIndex;
}
}
}

125
Guan/Common/Tri.cs Normal file
Просмотреть файл

@ -0,0 +1,125 @@
using System;
namespace Guan.Common
{
public class Tri<T> where T : class
{
class TriNode
{
private int level_;
private T value_;
private char begin_;
private TriNode[] children_;
public TriNode(int level)
{
level_ = level;
}
public void Add(string key, T value)
{
if (level_ == key.Length)
{
if (value_ != null)
{
throw new ArgumentException("Duplicate value for: " + key);
}
value_ = value;
}
else
{
int i;
if (children_ == null)
{
children_ = new TriNode[1];
begin_ = key[level_];
i = 0;
}
else
{
i = key[level_] - begin_;
if (i < 0)
{
TriNode[] children = new TriNode[children_.Length - i];
Array.Copy(children_, 0, children, -i, children_.Length);
children_ = children;
begin_ = key[level_];
i = 0;
}
else if (i >= children_.Length)
{
TriNode[] children = new TriNode[i + 1];
Array.Copy(children_, 0, children, 0, children_.Length);
children_ = children;
}
}
if (children_[i] == null)
{
children_[i] = new TriNode(level_ + 1);
}
children_[i].Add(key, value);
}
}
public T Get(string key, ref int start)
{
if (start + level_ == key.Length)
{
if (value_ != null)
{
start += level_;
}
return value_;
}
int i = key[start + level_] - begin_;
if (children_ != null && i >= 0 && i < children_.Length && children_[i] != null)
{
T result = children_[i].Get(key, ref start);
if (result != null)
{
return result;
}
}
if (value_ != null)
{
start += level_;
}
return value_;
}
}
private TriNode root_;
public Tri()
{
root_ = new TriNode(0);
}
public void Add(string key, T value)
{
root_.Add(key, value);
}
public T Get(string key, ref int start)
{
return root_.Get(key, ref start);
}
public T Get(string key)
{
int start = 0;
T result = Get(key, ref start);
if (start != key.Length)
{
return null;
}
return result;
}
}
}

453
Guan/Common/Utility.cs Normal file
Просмотреть файл

@ -0,0 +1,453 @@
using System;
using System.Configuration;
using System.Globalization;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace Guan.Common
{
public static class Utility
{
private readonly static string s_workingDirectoryPath = GetWorkingDirectoryPath();
private static string GetWorkingDirectoryPath()
{
string path = Utility.GetConfig("WorkingDirectoryPath", null);
if (path == null)
{
path = Path.GetDirectoryName(typeof(Utility).Assembly.Location);
}
return path;
}
/// <summary>
/// Get the full path of a trace configuration file.
/// The path is typically in the directory that contains
/// the trace executable.
/// </summary>
/// <param name="fileName">The file name.</param>
/// <returns>The full path of the file.</returns>
public static string GetFilePath(string fileName)
{
if (File.Exists(fileName))
{
return fileName;
}
return Path.Combine(s_workingDirectoryPath, fileName);
}
/// <summary>
/// Retrieve a string configuration entry from application
/// configuration file.
/// </summary>
/// <param name="name">Name of the entry.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns>The value of the entry, defaultValue if the
/// entry is not found.</returns>
public static string GetConfig(string name, string defaultValue = null)
{
string value = ConfigurationManager.AppSettings[name];
if (value == null)
{
return defaultValue;
}
return value;
}
/// <summary>
/// Retrieve a boolean configuration entry from application
/// configuration file.
/// </summary>
/// <param name="name">Name of the entry.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns>The value of the entry, defaultValue if the
/// entry is not found.</returns>
public static bool GetConfig(string name, bool defaultValue)
{
string value = ConfigurationManager.AppSettings[name];
if (value == null)
{
return defaultValue;
}
return bool.Parse(value);
}
/// <summary>
/// Retrieve an integer configuration entry from application
/// configuration file.
/// </summary>
/// <param name="name">Name of the entry.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns>The value of the entry, defaultValue if the
/// entry is not found.</returns>
public static int GetConfig(string name, int defaultValue)
{
string value = ConfigurationManager.AppSettings[name];
if (value == null)
{
return defaultValue;
}
return int.Parse(value, CultureInfo.InvariantCulture);
}
/// <summary>
/// Retrieve a TimeSpan configuration entry from application
/// configuration file.
/// </summary>
/// <param name="name">Name of the entry.</param>
/// <param name="defaultValue">The default value.</param>
/// <returns>The value of the entry, defaultValue if the
/// entry is not found.</returns>
public static TimeSpan GetConfig(string name, TimeSpan defaultValue)
{
string value = ConfigurationManager.AppSettings[name];
if (value == null)
{
return defaultValue;
}
return TimeSpan.Parse(value, CultureInfo.InvariantCulture);
}
/// <summary>
/// Format the time into a string.
/// </summary>
/// <param name="time">The time stamp.</param>
/// <returns>The string representation of the time stamp.</returns>
public static string FormatTime(DateTime time)
{
if (time == DateTime.MinValue)
{
return "MIN";
}
if (time == DateTime.MaxValue)
{
return "MAX";
}
return time.ToString("yyyy-M-d HH:mm:ss.fff", CultureInfo.InvariantCulture);
}
public static string FormatTimeWithTicks(DateTime time)
{
long ticks = time.Ticks % TimeSpan.TicksPerMillisecond;
string result = FormatTime(time);
if (ticks > 0 && time != DateTime.MaxValue && time != DateTime.MinValue)
{
result = result + "+" + ticks.ToString();
}
return result;
}
/// <summary>
/// Round down a time stamp to milli-second.
/// </summary>
/// <param name="time">The original time stamp.</param>
/// <returns>The time stamp after round down.</returns>
public static DateTime TimeRounddown(DateTime time)
{
long ticks = time.Ticks;
return new DateTime(ticks - ticks % TimeSpan.TicksPerMillisecond);
}
/// <summary>
/// Round up a time stamp to milli-second.
/// </summary>
/// <param name="time">The original time stamp.</param>
/// <returns>The time stamp after round up.</returns>
public static DateTime TimeRoundup(DateTime time)
{
long ticks = time.Ticks - 1;
return new DateTime(ticks - ticks % TimeSpan.TicksPerMillisecond + TimeSpan.TicksPerMillisecond);
}
public static bool TryParse(string value, out bool result)
{
result = (!string.IsNullOrEmpty(value) && value != "false");
return true;
}
public static bool TryParse(string value, out DateTime result)
{
if (string.Compare(value, "min", true) == 0)
{
result = DateTime.MinValue;
return true;
}
if (string.Compare(value, "max", true) == 0)
{
result = DateTime.MaxValue;
return true;
}
return DateTime.TryParse(value, out result);
}
public static bool TryParse(string value, Type type, out object result)
{
if (type == typeof(bool))
{
result = (!string.IsNullOrEmpty(value) && value != "false");
return true;
}
var converter = System.ComponentModel.TypeDescriptor.GetConverter(type);
if (converter == null)
{
result = null;
return false;
}
result = converter.ConvertFromString(value);
return true;
}
public static bool TryParse<T>(string value, out T result)
{
object obj;
bool success = TryParse(value, typeof(T), out obj);
result = (success ? (T)obj : default(T));
return success;
}
public static bool TryParse<T>(string value, T defaultValue, out T result)
{
if (string.IsNullOrEmpty(value))
{
result = defaultValue;
return true;
}
return Utility.TryParse<T>(value, out result);
}
public static bool TryConvert<T>(object value, out T result)
{
if (value == null)
{
result = default(T);
return true;
}
if (typeof(T).IsAssignableFrom(value.GetType()))
{
result = (T)value;
return true;
}
if (typeof(T) == typeof(GuanTime) && value is DateTime)
{
result = (T)(object)new GuanTime((DateTime)value);
return true;
}
return Utility.TryParse(value.ToString(), out result);
}
public static T Convert<T>(object value)
{
T result;
if (!TryConvert(value, out result))
{
ReleaseAssert.Fail("Can't convert {0} to {1}", value, typeof(T));
}
return result;
}
public static T MaxValue<T>()
{
if (typeof(T) == typeof(long))
{
return (T) (object) long.MaxValue;
}
else if (typeof(T) == typeof(ulong))
{
return (T) (object) ulong.MaxValue;
}
else if (typeof(T) == typeof(double))
{
return (T) (object) double.MaxValue;
}
throw new NotSupportedException(typeof(T).ToString());
}
public static T MinValue<T>()
{
if (typeof(T) == typeof(long))
{
return (T) (object) long.MinValue;
}
else if (typeof(T) == typeof(ulong))
{
return (T) (object) ulong.MinValue;
}
else if (typeof(T) == typeof(double))
{
return (T) (object) double.MinValue;
}
throw new NotSupportedException(typeof(T).ToString());
}
public static object CreateInstanceByReflection(string classSpec)
{
Type type = Type.GetType(classSpec);
if (type == null)
{
throw new ArgumentException("Can't find type: " + classSpec);
}
return Activator.CreateInstance(type);
}
public static string CollectionToString<T>(List<T> list)
{
StringBuilder result = new StringBuilder();
foreach (T item in list)
{
result.AppendFormat("{0},", item);
}
if (result.Length > 0)
{
result.Length--;
}
return result.ToString();
}
public static string FormatString(string format, params object[] args)
{
return string.Format(CultureInfo.InvariantCulture, format, args);
}
public static TimeSpan SafeSubtract(DateTime t1, DateTime t2)
{
if (t1 == DateTime.MaxValue)
{
ReleaseAssert.IsTrue(t2 != DateTime.MaxValue);
return TimeSpan.MaxValue;
}
if (t2 == DateTime.MinValue)
{
ReleaseAssert.IsTrue(t1 != DateTime.MinValue);
return TimeSpan.MaxValue;
}
return t1 - t2;
}
public static DateTime SafeSubtract(DateTime time, TimeSpan duration)
{
if (duration == TimeSpan.MaxValue)
{
return DateTime.MinValue;
}
if (time == DateTime.MaxValue || time == DateTime.MinValue)
{
return time;
}
return time - duration;
}
public static TimeSpan SafeSubtract(TimeSpan t1, TimeSpan t2)
{
if (t1 == TimeSpan.MaxValue || t2 == TimeSpan.MinValue)
{
return TimeSpan.MaxValue;
}
return t1 - t2;
}
public static DateTime SafeAdd(DateTime time, TimeSpan duration)
{
if (duration == TimeSpan.MaxValue)
{
return DateTime.MaxValue;
}
if (time == DateTime.MaxValue || time == DateTime.MinValue)
{
return time;
}
return time + duration;
}
public static DateTime Max(DateTime t1, DateTime t2)
{
return (t1 > t2 ? t1 : t2);
}
public static DateTime Min(DateTime t1, DateTime t2)
{
return (t1 < t2 ? t1 : t2);
}
public static TimeSpan Max(TimeSpan t1, TimeSpan t2)
{
return (t1 > t2 ? t1 : t2);
}
public static TimeSpan Min(TimeSpan t1, TimeSpan t2)
{
return (t1 < t2 ? t1 : t2);
}
public static TimeSpan AbsoluteValue(TimeSpan interval)
{
return (interval < TimeSpan.Zero ? -interval : interval);
}
public static bool TryCompare(object arg1, object arg2, out int result)
{
if (arg1 == null)
{
result = (arg2 == null ? 0 : -1);
return true;
}
else if (arg2 == null)
{
result = 1;
return true;
}
if (object.Equals(arg1, arg2))
{
result = 0;
return true;
}
IComparable comparable = arg1 as IComparable;
if (comparable != null)
{
//TODO: consider supporting args with different types (e.g. int vs. long)
result = comparable.CompareTo(arg2);
return true;
}
IPartialOrder partialOrder = arg1 as IPartialOrder;
if (partialOrder != null)
{
return partialOrder.CompareTo(arg2, out result);
}
result = 0;
return false;
}
}
}

17
Guan/Guan.csproj Normal file
Просмотреть файл

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>Guan</RootNamespace>
<AssemblyName>Guan</AssemblyName>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<None Include="Common\Guan.Common.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="System.Configuration.Abstractions" Version="2.0.2.45" />
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
</ItemGroup>
</Project>

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

@ -0,0 +1,51 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Guan.Logic
{
public class GuanQueryDispatcher
{
private Module module_;
public GuanQueryDispatcher(Module module)
{
module_ = module;
}
public async Task<bool> RunQueryAsync(string queryExpression)
{
ResolveOrder order = ResolveOrder.None;
QueryContext queryContext = new QueryContext();
queryContext.SetDirection(null, order);
ModuleProvider moduleProvider = new ModuleProvider();
moduleProvider.Add(module_);
Query query = Query.Create(
queryExpression,
queryContext,
moduleProvider);
await query.GetNextAsync().ConfigureAwait(false);
return true;
}
public async Task<bool> RunQueryAsync(List<CompoundTerm> queryExpression)
{
ResolveOrder order = ResolveOrder.None;
QueryContext queryContext = new QueryContext();
queryContext.SetDirection(null, order);
ModuleProvider moduleProvider = new ModuleProvider();
moduleProvider.Add(module_);
Query query = Query.Create(
queryExpression,
queryContext,
moduleProvider);
await query.GetNextAsync().ConfigureAwait(false);
return true;
}
}
}

7
Guan/IWaitingTask.cs Normal file
Просмотреть файл

@ -0,0 +1,7 @@
namespace Guan.Logic
{
public interface IWaitingTask
{
void Start();
}
}

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

@ -0,0 +1,17 @@
namespace Guan.Logic
{
/// <summary>
/// Meta data about term argument.
/// </summary>
public class ArgumentDescription
{
public string Text;
public bool Required;
public ArgumentDescription(string text, bool required)
{
Text = text;
Required = required;
}
}
}

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

@ -0,0 +1,49 @@
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Predicate type for assert.
/// </summary>
internal class AssertPredicateType : PredicateType
{
class Resolver : BooleanPredicateResolver
{
private bool append_;
public Resolver(bool append, CompoundTerm input, Constraint constraint, QueryContext context)
: base(input, constraint, context)
{
append_ = append;
}
protected override bool Check()
{
Term term = Input.Arguments[0].Value.GetEffectiveTerm();
CompoundTerm compound = term as CompoundTerm;
if (compound == null || !compound.IsGround())
{
throw new GuanException("Argument of assert must be a ground compound term: {0}", term);
}
Context.Assert((CompoundTerm)compound.GetGroundedCopy(), append_);
return true;
}
}
public static readonly AssertPredicateType Assert = new AssertPredicateType("assert");
public static readonly AssertPredicateType Asserta = new AssertPredicateType("asserta");
public static readonly AssertPredicateType Assertz = new AssertPredicateType("assertz");
private AssertPredicateType(string name)
: base(name, true, 1, 1)
{
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
return new Resolver(this != Asserta, input, constraint, context);
}
}
}

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

@ -0,0 +1,32 @@
using System.Threading.Tasks;
namespace Guan.Logic
{
/// <summary>
/// Resolver that acts as a boolean condition check with empty output.
/// </summary>
public abstract class BooleanPredicateResolver : PredicateResolver
{
public BooleanPredicateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
: base(input, constraint, context, 1)
{
}
protected abstract bool Check();
public override Task<UnificationResult> OnGetNextAsync()
{
UnificationResult result;
if (Check())
{
result = UnificationResult.Empty;
}
else
{
result = null;
}
return Task.FromResult<UnificationResult>(result);
}
}
}

503
Guan/Logic/CompoundTerm.cs Normal file
Просмотреть файл

@ -0,0 +1,503 @@
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Compound term.
/// A grounded compound term can be used as a dictionary.
/// </summary>
public class CompoundTerm : Term, IPropertyContext, IWritablePropertyContext, IEnumerable
{
private Functor functor_;
public VariableBinding binding_;
private List<TermArgument> arguments_;
private TermOption option_;
private static readonly List<TermArgument> EmptyArguments = new List<TermArgument>();
private const string EffectiveTypeArgumentName = "_EffectiveType";
public CompoundTerm()
: this(Functor.Empty, VariableBinding.Ground)
{
}
public CompoundTerm(string name)
: this(new Functor(name), VariableBinding.Ground)
{
}
public CompoundTerm(Functor functor)
: this(functor, VariableBinding.Ground)
{
}
public CompoundTerm(Functor functor, VariableBinding binding)
: this(functor, binding, EmptyArguments)
{
}
internal CompoundTerm(Functor functor, VariableBinding binding, List<TermArgument> arguments)
{
functor_ = functor;
binding_ = binding;
arguments_ = arguments;
option_ = TermOption.Default;
}
public Functor Functor
{
get
{
return functor_;
}
internal set
{
functor_ = value;
}
}
public PredicateType PredicateType
{
get
{
return (PredicateType)functor_;
}
}
internal override VariableBinding Binding
{
get
{
return binding_;
}
}
public List<TermArgument> Arguments
{
get
{
return arguments_;
}
}
public virtual IEnumerable<TermArgument> GetUnificationArgument()
{
return arguments_;
}
public IEnumerator GetEnumerator()
{
return arguments_.GetEnumerator();
}
public virtual Term GetArgument(string name)
{
int i = FindArgument(name);
if (i < 0)
{
return null;
}
return arguments_[i].Value;
}
public Term GetEffetiveType()
{
return GetArgument(EffectiveTypeArgumentName);
}
public void AddArgument(Term argument, string name, ArgumentDescription desc = null)
{
if (arguments_ == EmptyArguments)
{
arguments_ = new List<TermArgument>();
}
arguments_.Add(new TermArgument(name, argument, desc));
}
public bool RemoveArgument(string name)
{
int i = FindArgument(name);
if (i < 0)
{
return false;
}
arguments_.RemoveAt(i);
return true;
}
private int FindArgument(string name)
{
for (int i = 0; i < arguments_.Count; i++)
{
if (arguments_[i].Name == name)
{
return i;
}
}
return -1;
}
public virtual object this[string name]
{
get
{
CompoundTerm current = this;
int start = 0;
while (current != null)
{
int i = name.IndexOf('/', start);
if (i < 0)
{
Term term = current.GetArgument(name.Substring(start));
return term?.GetValue();
}
current = current.GetArgument(name.Substring(start, i - start)) as CompoundTerm;
start = i + 1;
}
return null;
}
set
{
CompoundTerm current = this;
int start = 0;
int i;
do
{
i = name.IndexOf('/', start);
if (i > 0)
{
string segment = name.Substring(start, i - start);
Term child = GetArgument(segment);
if (child == null)
{
CompoundTerm next = new CompoundTerm();
current.AddArgument(next, segment);
current = next;
}
else
{
current = child as CompoundTerm;
if (current == null)
{
throw new GuanException("Invalid path {0} to set value for {1}", name, this);
}
}
start = i + 1;
}
} while (i > 0);
if (start > 0)
{
name = name.Substring(start);
}
Term term = Term.FromObject(value);
i = current.FindArgument(name);
if (i < 0)
{
current.AddArgument(term, name);
}
else
{
current.Arguments[i].Value = term;
}
}
}
public TermOption Option
{
get
{
return option_;
}
}
public void SetOption(string name, object value)
{
if (option_ == TermOption.Default)
{
option_ = new TermOption();
}
option_[name] = value;
}
public override bool IsGround()
{
foreach (var arg in Arguments)
{
if (!arg.Value.IsGround())
{
return false;
}
}
return true;
}
public override Term GetEffectiveTerm()
{
EvaluatedFunctor func = functor_ as EvaluatedFunctor;
if (func != null && func.Func is StandaloneFunc)
{
return func.Evaluate(this, null);
}
Term term = GetArgument("this");
if (term != null)
{
// This arg should be a variable, when the variable is bound, it
// contains the result term.
CompoundTerm compound = term.GetEffectiveTerm() as CompoundTerm;
if (compound != null)
{
return compound;
}
}
return this;
}
internal CompoundTerm DuplicateInput(VariableBinding binding)
{
if (IsGround())
{
return this;
}
ReleaseAssert.IsTrue(binding_ != binding);
CompoundTerm result = new CompoundTerm(Functor, binding);
for (int i = 0; i < arguments_.Count; i++)
{
Term term = arguments_[i].Value.GetEffectiveTerm();
if (!term.IsGround())
{
Variable variable = term as Variable;
if (variable != null)
{
term = binding.AddOutputVariable(variable);
}
else
{
CompoundTerm compound = (CompoundTerm)term;
term = compound.DuplicateInput(binding);
}
}
result.AddArgument(term, arguments_[i].Name);
}
return result;
}
internal CompoundTerm DuplicateOutput(VariableBinding binding)
{
if (IsGround())
{
return this;
}
CompoundTerm result = new CompoundTerm(Functor, binding);
for (int i = 0; i < arguments_.Count; i++)
{
Term term = arguments_[i].Value.GetEffectiveTerm();
if (!term.IsGround())
{
Variable variable = term as Variable;
if (variable != null)
{
OutputVariable outputVariable = term as OutputVariable;
if (outputVariable != null)
{
term = outputVariable.Original;
}
else
{
term = binding.AddForeignVariable(variable);
}
}
else
{
CompoundTerm compound = (CompoundTerm)term;
term = compound.DuplicateOutput(binding);
}
}
result.AddArgument(term, arguments_[i].Name);
}
return result;
}
internal CompoundTerm DuplicateGoal(VariableBinding binding)
{
ReleaseAssert.IsTrue(Binding == VariableBinding.Ground);
CompoundTerm result = new CompoundTerm(Functor, binding);
for (int i = 0; i < arguments_.Count; i++)
{
Term term = arguments_[i].Value;
if (!term.IsGround())
{
IndexedVariable variable = term as IndexedVariable;
if (variable != null)
{
term = binding.GetLocalVariable(variable.Index);
}
else
{
CompoundTerm compound = (CompoundTerm)term;
term = compound.DuplicateGoal(binding);
}
}
result.AddArgument(term, arguments_[i].Name);
}
result.option_ = option_;
return result;
}
/// <summary>
/// Used during tail optimization to change the binding of the input goal
/// from the current binding of the rule to the previous one (that will be
/// used for output eventually) as the current binding is disappearing.
/// </summary>
/// <param name="binding">The binding for the output.</param>
internal void Migrate(VariableBinding binding)
{
for (int i = 0; i < arguments_.Count; i++)
{
Term term = arguments_[i].Value.GetEffectiveTerm();
CompoundTerm compound = term as CompoundTerm;
if (compound != null)
{
compound.Migrate(binding);
}
else
{
OutputVariable outputVariable = term as OutputVariable;
if (outputVariable != null)
{
ReleaseAssert.IsTrue(outputVariable.Original.Binding == binding);
arguments_[i].Value = outputVariable.Original;
}
else
{
Variable variable = term as Variable;
if (variable != null)
{
arguments_[i].Value = binding.AddForeignVariable(variable);
}
}
}
}
binding_ = binding;
}
internal EvaluatedFunctor GetEvaluatedFunctor()
{
EvaluatedFunctor evaluatedFunctor = functor_ as EvaluatedFunctor;
if (evaluatedFunctor == null)
{
ConstraintPredicateType constraintType = functor_ as ConstraintPredicateType;
if (constraintType != null)
{
evaluatedFunctor = constraintType.Func;
}
}
return evaluatedFunctor;
}
internal Term Evaluate(QueryContext context)
{
EvaluatedFunctor evaluatedFunctor = GetEvaluatedFunctor();
ReleaseAssert.IsTrue(evaluatedFunctor != null, "{0} can't be evaluted", this);
return evaluatedFunctor.Evaluate(this, context);
}
internal void PostProcessing(Rule rule)
{
ProcessOption(rule);
try
{
PredicateType.AdjustTerm(this, rule);
}
catch (GuanException e)
{
throw new GuanException(e, "Invalid {0} {1} in rule: {2}", this == rule.Head ? "head" : "goal", this, rule);
}
}
private void ProcessOption(Rule rule)
{
for (int i = 0; i < arguments_.Count; i++)
{
CompoundTerm term = arguments_[i].Value as CompoundTerm;
if (term != null && term.Functor.Name == "_option")
{
if (this == rule.Head)
{
throw new GuanException("_option can't be used in head");
}
option_ = new TermOption(term);
arguments_.RemoveAt(i);
return;
}
}
}
internal void OutputArguments(StringBuilder result)
{
for (int i = 0; i < Arguments.Count; i++)
{
CompoundTerm compound = arguments_[i].Value as CompoundTerm;
if ((arguments_[i].Name == i.ToString()) || (compound != null && compound.Functor.Name == arguments_[i].Name))
{
result.AppendFormat("{0},", arguments_[i].Value);
}
else if (!Arguments[i].Name.StartsWith("_"))
{
result.AppendFormat("{0}={1},", Arguments[i].Name, arguments_[i].Value);
}
}
}
public override string ToString()
{
if (functor_.Name == ".")
{
return ListTerm.ToString(this);
}
StringBuilder result = new StringBuilder();
result.AppendFormat("{0}(", functor_);
OutputArguments(result);
if (Arguments.Count > 0)
{
result.Length--;
}
result.Append(')');
return result.ToString();
}
}
}

110
Guan/Logic/Constant.cs Normal file
Просмотреть файл

@ -0,0 +1,110 @@
using System;
namespace Guan.Logic
{
/// <summary>
/// Constant term.
/// </summary>
public class Constant : Term
{
private object value_;
/// <summary>
/// Wrapping a null object.
/// </summary>
public static readonly Constant Null = new Constant(null);
/// <summary>
/// Empty list.
/// </summary>
public static readonly Constant Nil = new Constant(new object());
public Constant(object value)
{
value_ = value;
}
public object Value
{
get
{
return value_;
}
}
public override bool IsGround()
{
return true;
}
internal bool IsTrue()
{
if (value_ == null)
{
return false;
}
if (value_ is bool)
{
return (bool)value_;
}
string s = value_ as string;
if (s != null)
{
return s.Length > 0;
}
return true;
}
public override string ToString()
{
if (this == Nil)
{
return "[]";
}
return (value_ != null ? value_.ToString() : "");
}
public static Constant Parse(string text)
{
if (text.StartsWith("'") && text.EndsWith("'") && text.Length > 1)
{
return new Constant(text.Substring(1, text.Length - 2));
}
if (text == "null")
{
return Null;
}
long longValue;
if (long.TryParse(text, out longValue))
{
return new Constant(longValue);
}
ulong ulongValue;
if (ulong.TryParse(text, out ulongValue))
{
return new Constant(ulongValue);
}
double doubleValue;
if (double.TryParse(text, out doubleValue))
{
return new Constant(doubleValue);
}
TimeSpan timeSpan;
if (TimeSpan.TryParse(text, out timeSpan))
{
return new Constant(timeSpan);
}
return new Constant(text);
}
}
}

474
Guan/Logic/Constraint.cs Normal file
Просмотреть файл

@ -0,0 +1,474 @@
using System.Collections.Generic;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// A constraint is a collection of criteria (each represented as an evalutable
/// compound) that can be validated.
/// Other than the criteria, separate entries for upper/lower bound and possible
/// values are maintained so that the constraint can be used not only for passive
/// validation, but also actively queried by the resolvers for more efficient
/// search.
/// </summary>
public class Constraint
{
class BoundEntry
{
private CompoundTerm term_;
private Variable variable_;
private object value_;
private bool isInclusive_;
public BoundEntry(CompoundTerm term, Variable variable, object value, bool isInclusive)
{
term_ = term;
variable_ = variable;
value_ = value;
isInclusive_ = isInclusive;
}
public CompoundTerm Term
{
get
{
return term_;
}
}
public object Get(Variable variable, out bool isInclusive)
{
if (variable != variable_.GetEffectiveTerm())
{
isInclusive = false;
return null;
}
isInclusive = isInclusive_;
return value_;
}
}
class ValueEntry
{
private CompoundTerm term_;
private Variable variable_;
private List<object> values_;
public ValueEntry(CompoundTerm term)
{
term_ = term;
values_ = new List<object>();
}
public ValueEntry(CompoundTerm term, Variable variable, object value)
{
term_ = term;
variable_ = variable;
values_ = new List<object>(1)
{
value
};
}
public CompoundTerm Term
{
get
{
return term_;
}
}
public bool Add(RawEntry entry)
{
if (entry.Func != ComparisonFunc.EQ)
{
return false;
}
if (variable_ == null)
{
variable_ = entry.Variable;
}
else if (variable_ != entry.Variable)
{
return false;
}
values_.Add(entry.Value);
return true;
}
public List<object> Get(Variable variable)
{
if (variable != variable_.GetEffectiveTerm())
{
return null;
}
return values_;
}
}
/// <summary>
/// Terms like "variable comparison constant" or "constant comparison variable" only
/// </summary>
class RawEntry
{
private Variable variable_;
private object value_;
private ComparisonFunc func_;
private RawEntry(Variable variable, object value, ComparisonFunc func)
{
variable_ = variable;
value_ = value;
func_ = func;
}
public Variable Variable
{
get
{
return variable_;
}
}
public object Value
{
get
{
return value_;
}
}
public ComparisonFunc Func
{
get
{
return func_;
}
}
public void AddToConstaint(Constraint constraint, CompoundTerm term)
{
if (func_ == ComparisonFunc.EQ)
{
constraint.AddValueEntry(new ValueEntry(term, variable_, value_));
}
else if (func_ == ComparisonFunc.LT || func_ == ComparisonFunc.LE)
{
constraint.AddUpperBoundEntry(new BoundEntry(term, variable_, value_, func_ == ComparisonFunc.LE));
}
else if (func_ == ComparisonFunc.GT || func_ == ComparisonFunc.GE)
{
constraint.AddLowerBoundEntry(new BoundEntry(term, variable_, value_, func_ == ComparisonFunc.GE));
}
}
public static RawEntry Convert(CompoundTerm term)
{
EvaluatedFunctor evaluatedFunctor = term.GetEvaluatedFunctor();
if (evaluatedFunctor == null || term.Arguments.Count != 2)
{
return null;
}
ComparisonFunc func = evaluatedFunctor.Func as ComparisonFunc;
if (func == null)
{
return null;
}
Term term1 = term.Arguments[0].Value.GetEffectiveTerm();
Term term2 = term.Arguments[1].Value.GetEffectiveTerm();
Constant constant = null;
Variable variable = term1 as Variable;
if (variable != null)
{
constant = term2 as Constant;
}
else
{
constant = term1 as Constant;
variable = term2 as Variable;
func = func.Inverse();
}
if (variable == null || constant == null)
{
return null;
}
return new RawEntry(variable, constant.Value, func);
}
}
private List<CompoundTerm> terms_;
private List<BoundEntry> lowerEntries_;
private List<BoundEntry> upperEnties_;
private List<ValueEntry> valueEntries_;
public static readonly Constraint Empty = new Constraint();
internal Constraint()
{
terms_ = new List<CompoundTerm>();
}
internal List<CompoundTerm> Terms
{
get
{
return terms_;
}
}
internal void Add(CompoundTerm term)
{
Process(term);
}
internal void Add(IEnumerable<CompoundTerm> terms)
{
foreach (CompoundTerm term in terms)
{
Process(term);
}
}
internal int Evaluate(QueryContext context)
{
int result = 1;
foreach (CompoundTerm term in terms_)
{
Constant evaluted = term.Evaluate(context) as Constant;
if (evaluted == null)
{
result = 0;
}
else if (!evaluted.IsTrue())
{
return -1;
}
}
return result;
}
private void Process(CompoundTerm term)
{
EvaluatedFunctor evaluatedFunctor = term.GetEvaluatedFunctor();
bool add = true;
if (evaluatedFunctor != null)
{
if (evaluatedFunctor.Func is AndFunc)
{
foreach (var arg in term.Arguments)
{
CompoundTerm compound = arg.Value as CompoundTerm;
if (compound != null)
{
Process(compound);
}
}
add = false;
}
if (evaluatedFunctor.Func is OrFunc)
{
ValueEntry entry = new ValueEntry(term);
// We will only handle disjunction of a collectoin of comparisons
// with the same variable.
if (DecomposeDisjunction(term, entry))
{
AddValueEntry(entry);
add = false;
}
}
else
{
RawEntry rawEntry = RawEntry.Convert(term);
if (rawEntry != null)
{
rawEntry.AddToConstaint(this, term);
}
}
}
if (add)
{
terms_.Add(term);
}
}
private bool DecomposeDisjunction(CompoundTerm term, ValueEntry entry)
{
foreach (var arg in term.Arguments)
{
CompoundTerm compound = arg.Value as CompoundTerm;
if (compound == null)
{
return false;
}
EvaluatedFunctor evaluatedFunctor = compound.GetEvaluatedFunctor();
if (evaluatedFunctor.Func is OrFunc)
{
if (!DecomposeDisjunction(compound, entry))
{
return false;
}
}
else
{
RawEntry rawEntry = RawEntry.Convert(compound);
if (rawEntry == null || !entry.Add(rawEntry))
{
return false;
}
}
}
return true;
}
private void AddValueEntry(ValueEntry entry)
{
if (valueEntries_ == null)
{
valueEntries_ = new List<ValueEntry>();
}
valueEntries_.Add(entry);
}
private void AddUpperBoundEntry(BoundEntry entry)
{
if (upperEnties_ == null)
{
upperEnties_ = new List<BoundEntry>();
}
upperEnties_.Add(entry);
}
private void AddLowerBoundEntry(BoundEntry entry)
{
if (lowerEntries_ == null)
{
lowerEntries_ = new List<BoundEntry>();
}
lowerEntries_.Add(entry);
}
public List<object> GetValues(Variable variable, bool remove)
{
if (valueEntries_ == null)
{
return null;
}
List<object> result = null;
for (int i = valueEntries_.Count - 1; i >= 0; i--)
{
List<object> values = valueEntries_[i].Get(variable);
if (values != null)
{
if (result == null)
{
result = values;
}
else
{
MergeValues(result, values);
}
if (remove)
{
terms_.Remove(valueEntries_[i].Term);
valueEntries_.RemoveAt(i);
}
}
}
return result;
}
private static void MergeValues(List<object> list1, List<object> list2)
{
for (int i = list1.Count - 1; i >= 0; i--)
{
bool found = false;
for (int j = 0; j < list2.Count && !found; j++)
{
found = object.Equals(list1[i], list2[j]);
}
if (!found)
{
list1.RemoveAt(i);
}
}
}
public object GetLowerBound(Variable variable, bool remove, out bool isInclusive)
{
return GetBound(false, variable, remove, out isInclusive);
}
public object GetUpperBound(Variable variable, bool remove, out bool isInclusive)
{
return GetBound(true, variable, remove, out isInclusive);
}
private object GetBound(bool upper, Variable variable, bool remove, out bool isInclusive)
{
isInclusive = false;
List<BoundEntry> entries = (upper ? upperEnties_ : lowerEntries_);
if (entries == null)
{
return null;
}
object result = null;
for (int i = entries.Count - 1; i >= 0; i--)
{
bool inclusive;
object value = entries[i].Get(variable, out inclusive);
if (value != null)
{
if (result == null || CompareResult(value, inclusive, result, isInclusive, upper))
{
result = value;
isInclusive = inclusive;
}
if (remove)
{
terms_.Remove(entries[i].Term);
entries.RemoveAt(i);
}
}
}
return result;
}
private bool CompareResult(object value1, bool inclusive1, object value2, bool inclusive2, bool upper)
{
if (object.Equals(value1, value2))
{
return (!inclusive1 && inclusive2);
}
ComparisonFunc func = (upper ? ComparisonFunc.LT : ComparisonFunc.GT);
return func.Invoke(value1, value2);
}
}
}

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

@ -0,0 +1,62 @@
using System.Threading.Tasks;
namespace Guan.Logic
{
internal class ConstraintPredicateType : PredicateType
{
class Resolver : PredicateResolver
{
private EvaluatedFunctor func_;
public Resolver(EvaluatedFunctor func, CompoundTerm input, Constraint constraint, QueryContext context)
: base(input, constraint, context)
{
func_ = func;
}
public override Task<UnificationResult> OnGetNextAsync()
{
UnificationResult result = null;
if (Iteration == 0)
{
Term term = func_.Evaluate(Input, Context);
Constant constant = term as Constant;
if (constant == null)
{
result = new UnificationResult(0);
result.AddConstraint(Input);
}
else if (constant.IsTrue())
{
result = UnificationResult.Empty;
}
Complete();
}
return Task.FromResult(result);
}
}
private EvaluatedFunctor func_;
public ConstraintPredicateType(EvaluatedFunctor evaluatedFunctor)
: base(evaluatedFunctor.Name)
{
func_ = evaluatedFunctor;
}
public EvaluatedFunctor Func
{
get
{
return func_;
}
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
return new Resolver(func_, input, constraint, context);
}
}
}

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

@ -0,0 +1,33 @@
namespace Guan.Logic
{
/// <summary>
/// Predicate tyoe for cut(!).
/// </summary>
internal class CutPredicateType : PredicateType
{
class Resolver : BooleanPredicateResolver
{
public Resolver()
: base(null, null, null)
{
}
protected override bool Check()
{
return true;
}
}
public static readonly CutPredicateType Singleton = new CutPredicateType();
private CutPredicateType()
: base("!")
{
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
return new Resolver();
}
}
}

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

@ -0,0 +1,68 @@
using System.Collections;
using System.Threading.Tasks;
namespace Guan.Logic
{
/// <summary>
/// Predicate type for enumeration of collection object.
/// </summary>
internal class EnumerablePredicateType : PredicateType
{
class Resolver : PredicateResolver
{
private IEnumerable collection_;
private IEnumerator enumerator_;
private VariableBinding binding_;
public Resolver(CompoundTerm input, Constraint constraint, QueryContext context)
: base(input, constraint, context)
{
Term term = input.Arguments[0].Value.GetEffectiveTerm();
Constant constant = term as Constant;
if (constant != null)
{
collection_ = constant.Value as IEnumerable;
}
else
{
collection_ = term as IEnumerable;
}
if (collection_ != null)
{
enumerator_ = collection_.GetEnumerator();
binding_ = new VariableBinding(VariableTable.Empty, 0, input.Binding.Level + 1);
}
}
public override Task<UnificationResult> OnGetNextAsync()
{
UnificationResult result = null;
while (result == null && enumerator_ != null && enumerator_.MoveNext())
{
Term term = Term.FromObject(enumerator_.Current);
if (binding_.Unify(term, Input.Arguments[1].Value))
{
result = binding_.CreateOutput();
}
binding_.ResetOutput();
}
return Task.FromResult<UnificationResult>(result);
}
}
public static readonly EnumerablePredicateType Singleton = new EnumerablePredicateType();
private EnumerablePredicateType()
: base("enumerable", true, 2, 2)
{
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
return new Resolver(input, constraint, context);
}
}
}

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

@ -0,0 +1,54 @@
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Functor that can transform the containing compound term.
/// </summary>
internal class EvaluatedFunctor : Functor
{
private GuanFunc func_;
private ConstraintPredicateType constraintType_;
public EvaluatedFunctor(GuanFunc func)
: base(func.Name)
{
func_ = func;
constraintType_ = new ConstraintPredicateType(this);
}
public GuanFunc Func
{
get
{
return func_;
}
}
public ConstraintPredicateType ConstraintType
{
get
{
return constraintType_;
}
}
public Term Evaluate(CompoundTerm term, QueryContext context)
{
object[] args = new object[term.Arguments.Count];
for (int i = 0; i < args.Length; i++)
{
Term arg = term.Arguments[i].Value.GetEffectiveTerm();
if (!arg.IsGround())
{
return term;
}
args[i] = arg.GetValue();
}
object result = func_.Invoke(context, args);
return Term.FromObject(result);
}
}
}

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

@ -0,0 +1,34 @@
namespace Guan.Logic
{
/// <summary>
/// Predicate type for "fail"
/// </summary>
internal class FailPredicateType : PredicateType
{
class Resolver : BooleanPredicateResolver
{
public Resolver()
: base(null, null, null)
{
}
protected override bool Check()
{
return false;
}
}
public static readonly FailPredicateType Singleton = new FailPredicateType();
public static readonly FailPredicateType NotApplicable = new FailPredicateType();
private FailPredicateType()
: base("fail")
{
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
return new Resolver();
}
}
}

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

@ -0,0 +1,48 @@
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Predicate type for forwardcut.
/// </summary>
internal class ForwardCutPredicateType : PredicateType
{
class Resolver : BooleanPredicateResolver
{
public Resolver(QueryContext context)
: base(null, null, context)
{
}
protected override bool Check()
{
return true;
}
}
public static readonly ForwardCutPredicateType Singleton = new ForwardCutPredicateType();
private ForwardCutPredicateType()
: base("forwardcut", true, 0, 0)
{
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
return new Resolver(context);
}
public override void AdjustTerm(CompoundTerm term, Rule rule)
{
base.AdjustTerm(term, rule);
int i;
for (i = 0; i < rule.Goals.Count && term != rule.Goals[i]; i++) ;
if (i == 0 || i >= rule.Goals.Count -1 || rule.Goals[i - 1].PredicateType is ConstraintPredicateType)
{
throw new GuanException("Invalid use of forwardcut");
}
}
}
}

115
Guan/Logic/Functor.cs Normal file
Просмотреть файл

@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Functor for compound term.
/// </summary>
public class Functor
{
private string name_;
private List<KeyValuePair<string, ArgumentDescription>> args_;
private List<string> requiredArguments_;
private static readonly Regex NamePattern = new Regex(@"^[_\w]+$", RegexOptions.Compiled);
private static readonly List<KeyValuePair<string, ArgumentDescription>> EmptyArgs = new List<KeyValuePair<string, ArgumentDescription>>();
private static readonly List<string> EmptyRequiredArgs = new List<string>();
public static readonly Functor ClassObject = new Functor("object");
public static readonly Functor Empty = new Functor("");
public Functor(string name)
{
name_ = name;
args_ = EmptyArgs;
requiredArguments_ = EmptyRequiredArgs;
}
public string Name
{
get
{
return name_;
}
}
public ArgumentDescription GetArgumentDescription(string name)
{
foreach (var arg in args_)
{
if (arg.Key == name)
{
return arg.Value;
}
}
return null;
}
public List<string> RequiredArguments
{
get
{
return requiredArguments_;
}
}
public void AddArgumentDescription(string name, ArgumentDescription arg)
{
if (GetArgumentDescription(name) != null)
{
throw new GuanException("Duplicate argument description for {0} in {1}", name, this);
}
if (args_ == EmptyArgs)
{
args_ = new List<KeyValuePair<string, ArgumentDescription>>();
}
args_.Add(new KeyValuePair<string, ArgumentDescription>(name, arg));
if (arg.Required)
{
if (requiredArguments_ == EmptyRequiredArgs)
{
requiredArguments_ = new List<string>();
}
requiredArguments_.Add(name);
}
}
public virtual bool Unify(Functor other)
{
return (name_ == other.name_);
}
public override bool Equals(object obj)
{
Functor other = obj as Functor;
return (other != null && name_ == other.name_);
}
public override int GetHashCode()
{
return name_.GetHashCode();
}
public override string ToString()
{
return name_;
}
internal static Functor Parse(string name)
{
if (PredicateType.GetBuiltInType(name) == null && !NamePattern.IsMatch(name))
{
throw new ArgumentException("Invalid functor name: " + name);
}
return new Functor(name);
}
}
}

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

@ -0,0 +1,41 @@
using System.Collections.Generic;
namespace Guan.Logic
{
/// <summary>
/// Container for functors.
/// </summary>
public class FunctorTable : IFunctorProvider
{
private IFunctorProvider provider_;
private Dictionary<string, Functor> functors_;
public FunctorTable(IFunctorProvider provider = null)
{
provider_ = provider;
functors_ = new Dictionary<string, Functor>();
}
public void Add(Functor functor)
{
lock (functors_)
{
functors_.Add(functor.Name, functor);
}
}
public Functor FindFunctor(string name, Module from)
{
lock (functors_)
{
Functor result;
if (!functors_.TryGetValue(name, out result) && provider_ != null)
{
result = provider_.FindFunctor(name, from);
}
return result;
}
}
}
}

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

@ -0,0 +1,55 @@
using System.Threading.Tasks;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Predicate type for getval.
/// </summary>
internal class GetValPredicateType : PredicateType
{
class Resolver : GroundPredicateResolver
{
public Resolver(CompoundTerm input, Constraint constraint, QueryContext context)
: base(input, constraint, context, 1)
{
}
protected override Task<Term> GetNextTermAsync()
{
string name = Input.Arguments[0].Value.GetStringValue();
object value = Context[name];
CompoundTerm result = new CompoundTerm(GetValPredicateType.Singleton, null);
result.AddArgument(new Constant(value), "1");
return Task.FromResult<Term>(result);
}
}
public static readonly GetValPredicateType Singleton = new GetValPredicateType();
private GetValPredicateType()
: base("getval", true, 2, 2)
{
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
return new Resolver(input, constraint, context);
}
public override void AdjustTerm(CompoundTerm term, Rule rule)
{
string name = term.Arguments[0].Value.GetStringValue();
if (name == null)
{
throw new GuanException("The first argument of getval must be string: {0}", term);
}
if (!(term.Arguments[1].Value is IndexedVariable))
{
throw new GuanException("The second argument of getval must be a variable: {0}", term);
}
}
}
}

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

@ -0,0 +1,45 @@
using System.Threading.Tasks;
namespace Guan.Logic
{
/// <summary>
/// Resolver that uses grounded term to unify with the goal.
/// This is typically resolver of external predicate which uses the goal
/// as a query against concrete instances of data.
/// </summary>
public abstract class GroundPredicateResolver : PredicateResolver
{
private VariableBinding binding_;
public GroundPredicateResolver(CompoundTerm input, Constraint constraint, QueryContext context, int max = int.MaxValue)
: base(input, constraint, context, max)
{
binding_ = new VariableBinding(VariableTable.Empty, 0, input.Binding.Level + 1);
}
public override async Task<UnificationResult> OnGetNextAsync()
{
UnificationResult result = null;
while (result == null && !Completed)
{
Term term = await GetNextTermAsync();
if (term == null)
{
return null;
}
if (binding_.Unify(term, Input))
{
result = binding_.CreateOutput();
}
binding_.ResetOutput();
}
return result;
}
protected abstract Task<Term> GetNextTermAsync();
}
}

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

@ -0,0 +1,10 @@
namespace Guan.Logic
{
/// <summary>
/// Interface for exporting object to compound term.
/// </summary>
public interface ICompoundTerm
{
CompoundTerm ToCompoundTerm();
}
}

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

@ -0,0 +1,10 @@
namespace Guan.Logic
{
/// <summary>
/// Interface providing functor (including predicate type) implementations.
/// </summary>
public interface IFunctorProvider
{
Functor FindFunctor(string name, Module from);
}
}

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

@ -0,0 +1,43 @@
namespace Guan.Logic
{
/// <summary>
/// Variable term used in rules. At runtime they will be converted to Variable objects.
/// </summary>
public class IndexedVariable : Term
{
private int index_;
private string name_;
public IndexedVariable(int index, string name)
{
index_ = index;
name_ = name;
}
public int Index
{
get
{
return index_;
}
}
public string Name
{
get
{
return name_;
}
}
public override bool IsGround()
{
return false;
}
public override string ToString()
{
return "?" + name_ + "_" + index_.ToString();
}
}
}

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

@ -0,0 +1,39 @@
using System.Threading.Tasks;
namespace Guan.Logic
{
/// <summary>
/// Predicate type for "is".
/// </summary>
internal class IsPredicateType : PredicateType
{
class Resolver : GroundPredicateResolver
{
public Resolver(CompoundTerm input, Constraint constraint, QueryContext context)
: base(input, constraint, context)
{
}
protected override Task<Term> GetNextTermAsync()
{
Term term = Input.Arguments[1].Value.ForceEvaluate(Context);
CompoundTerm result = new CompoundTerm(Singleton, null);
result.AddArgument(term, "0");
return Task.FromResult<Term>(result);
}
}
public static readonly IsPredicateType Singleton = new IsPredicateType();
private IsPredicateType()
: base("is", true, 2, 2)
{
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
return new Resolver(input, constraint, context);
}
}
}

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

@ -0,0 +1,50 @@
namespace Guan.Logic
{
/// <summary>
/// A variable bound to another variable.
/// </summary>
public class LinkedVariable : Variable
{
private Variable original_;
public LinkedVariable(VariableBinding binding, Variable original, string name)
: base(name, binding)
{
original_ = original;
}
public LinkedVariable(LinkedVariable other, VariableBinding binding)
: base(other, binding)
{
original_ = other.original_;
}
public Variable Original
{
get
{
return original_;
}
set
{
original_ = value;
}
}
}
/// <summary>
/// Variable output from a goal to the rule.
/// </summary>
public class OutputVariable : LinkedVariable
{
public OutputVariable(VariableBinding binding, Variable original, string name)
: base(binding, original, name)
{
}
public OutputVariable(OutputVariable other, VariableBinding binding)
: base(other, binding)
{
}
}
}

133
Guan/Logic/ListTerm.cs Normal file
Просмотреть файл

@ -0,0 +1,133 @@
using System.Collections;
using System.Text;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Logic specific to list.
/// </summary>
public static class ListTerm
{
public static Functor ListFunctor = new Functor(".");
public static CompoundTerm Add(CompoundTerm current, Term child)
{
if (current.Arguments.Count == 0)
{
current.AddArgument(child, "0");
return current;
}
ReleaseAssert.IsTrue(current.Arguments.Count == 1);
CompoundTerm tail = new CompoundTerm(ListFunctor, null);
current.AddArgument(tail, "1");
tail.AddArgument(child, "0");
return tail;
}
public static string ToString(CompoundTerm term)
{
StringBuilder result = new StringBuilder();
result.Append("[");
CompoundTerm current = term;
Term next;
do
{
result.Append(current.Arguments[0].Value).Append(',');
next = current.Arguments[1].Value.GetEffectiveTerm();
current = next as CompoundTerm;
} while (current != null && current.Functor.Name == ".");
result.Length--;
if (next == Constant.Nil)
{
result.Append("]");
}
else
{
result.AppendFormat("|{0}]", next);
}
return result.ToString();
}
public static Term Parse(CompoundTerm term)
{
if (term.Arguments.Count == 0)
{
return Constant.Nil;
}
Term head = term.Arguments[0].Value;
Term tail;
ReleaseAssert.IsTrue(term.Arguments.Count == 1);
CompoundTerm compound = head as CompoundTerm;
if (compound != null && compound.Functor.Name == "|")
{
ReleaseAssert.IsTrue(compound.Arguments.Count == 2);
head = compound.Arguments[0].Value;
tail = compound.Arguments[1].Value;
}
else
{
tail = Constant.Nil;
}
CompoundTerm result = new CompoundTerm(ListTerm.ListFunctor, VariableBinding.Ground);
CompoundTerm current = result;
while (head != null)
{
compound = head as CompoundTerm;
if (compound != null && compound.Functor.Name == ",")
{
ReleaseAssert.IsTrue(compound.Arguments.Count == 2);
current.AddArgument(compound.Arguments[0].Value, "0");
head = compound.Arguments[1].Value;
CompoundTerm next = new CompoundTerm(ListTerm.ListFunctor, VariableBinding.Ground);
current.AddArgument(next, "1");
current = next;
}
else
{
current.AddArgument(head, "0");
head = null;
}
}
current.AddArgument(tail, "1");
return result;
}
public static Term FromEnumerable(IEnumerable collection)
{
Term result = Constant.Nil;
CompoundTerm current = null;
foreach (object member in collection)
{
CompoundTerm next = new CompoundTerm(ListFunctor);
if (current != null)
{
current.AddArgument(next, "1");
}
else
{
result = next;
}
current = next;
current.AddArgument(Term.FromObject(member), "0");
}
if (current != null)
{
current.AddArgument(Constant.Nil, "1");
}
return result;
}
}
}

363
Guan/Logic/Module.cs Normal file
Просмотреть файл

@ -0,0 +1,363 @@
using System.Collections.Generic;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// A module is a collection of predicate types.
/// </summary>
public class Module
{
private string name_;
private bool dynamic_;
private Dictionary<string, PredicateType> types_;
private static readonly Module SystemModule = CreateSystemModule();
internal Module(string name)
{
name_ = name;
types_ = new Dictionary<string, PredicateType>();
dynamic_ = true;
}
private Module(string name, Dictionary<string, PredicateType> types)
{
name_ = name;
types_ = types;
dynamic_ = false;
}
public string Name
{
get
{
return name_;
}
}
/// <summary>
/// Add a dynamic predicate (assert).
/// </summary>
/// <param name="term">The predicate.</param>
/// <param name="append">Whether to add the predicate in append mode.</param>
public void Add(CompoundTerm term, bool append)
{
if (!dynamic_)
{
throw new GuanException("Not a dynamic module");
}
lock (types_)
{
PredicateType type;
if (!types_.TryGetValue(term.Functor.Name, out type))
{
type = new PredicateType(term.Functor.Name, true);
types_.Add(term.Functor.Name, type);
}
CompoundTerm head = new CompoundTerm(type, VariableBinding.Ground, term.Arguments);
Rule rule = new Rule(term.ToString(), head, new List<CompoundTerm>(), VariableTable.Empty);
type.AddRule(this, rule, append);
}
}
public void Add(PredicateType predicateType)
{
lock (types_)
{
types_[predicateType.Name] = predicateType;
}
}
public IEnumerable<PredicateType> GetPublicTypes()
{
List<PredicateType> result = new List<PredicateType>();
foreach (var type in types_)
{
if (type.Value.IsPublic)
{
result.Add(type.Value);
}
}
return result;
}
public PredicateType GetPredicateType(string name)
{
PredicateType result;
if (types_.TryGetValue(name, out result) && result.IsPublic)
{
return result;
}
return null;
}
public override string ToString()
{
return name_;
}
public static Module Parse(string name, List<string> ruleExpressions, IFunctorProvider provider, List<string> publicTypes = null)
{
List<Rule> rules = new List<Rule>(ruleExpressions.Count);
foreach (string ruleExpression in ruleExpressions)
{
Rule rule;
try
{
rule = Rule.Parse(ruleExpression);
rules.Add(rule);
}
catch (GuanException e)
{
throw new GuanException(e, "Fail to parse rule {0} in module {1}", ruleExpression, name);
}
}
return Parse(name, rules, provider, publicTypes);
}
internal static Module Parse(string name, List<Rule> rules, IFunctorProvider provider, List<string> publicTypes)
{
Dictionary<string, PredicateType> types = new Dictionary<string, PredicateType>();
Module result = new Module(name, types);
for (int i = 0; i < rules.Count;)
{
string typeName = rules[i].Head.Functor.Name;
if (typeName != "desc")
{
PredicateType type;
if (!types.TryGetValue(typeName, out type))
{
if (provider != null)
{
type = provider.FindFunctor(typeName, result) as PredicateType;
}
if (type == null)
{
bool isPublic = (publicTypes != null ? publicTypes.Contains(typeName) : !typeName.StartsWith("_"));
type = new PredicateType(typeName, isPublic);
}
types.Add(typeName, type);
}
rules[i].Head.Functor = type;
UpdateFunctor(rules[i].Head, types, provider, result);
i++;
}
else
{
ProcessDesc(rules[i], types);
rules.RemoveAt(i);
}
}
for (int i = 0; i < rules.Count; i++)
{
bool remove = false;
foreach (CompoundTerm goal in rules[i].Goals)
{
string typeName = goal.Functor.Name;
PredicateType type;
if (!types.TryGetValue(typeName, out type))
{
type = GetGoalPredicateType(typeName, provider, result);
if (type == FailPredicateType.NotApplicable)
{
remove = true;
}
else if (type == null)
{
throw new GuanException("Predicate type {0} not defined in rule {1}", typeName, rules[i]);
}
}
goal.Functor = type;
UpdateFunctor(goal, types, provider, result);
}
if (!remove)
{
try
{
rules[i].PostProcessing();
}
catch (GuanException e)
{
throw new GuanException(e, "Fail to process rule {0}", rules[i]);
}
rules[i].Head.PredicateType.AddRule(result, rules[i], true);
}
}
return result;
}
private static void ProcessDesc(Rule rule, Dictionary<string, PredicateType> types)
{
if (rule.Goals.Count > 0 || rule.Head.Arguments.Count < 2 || !(rule.Head.Arguments[0].Value is Constant))
{
throw new GuanException("Invalid desc predicate: {0}", rule);
}
Constant constant = (Constant)rule.Head.Arguments[0].Value;
PredicateType type;
string typeName = constant.GetStringValue();
if (typeName == null || !types.TryGetValue(typeName, out type))
{
if (rule.Head.Arguments.Count == 2)
{
Constant arg = rule.Head.Arguments[1].Value as Constant;
if (arg != null && arg.GetStringValue() == "dynamic")
{
types.Add(typeName, new DynamicPredicateType(typeName));
return;
}
}
throw new GuanException("Type {0} in desc predicate not defined", constant);
}
for (int i = 1; i < rule.Head.Arguments.Count; i++)
{
string name = rule.Head.Arguments[i].Name;
if (name == i.ToString())
{
name = null;
}
try
{
type.ProcessMetaData(name, rule.Head.Arguments[i].Value);
}
catch (GuanException e)
{
throw new GuanException(e, "Meta-data {0} {1} can't be applied to {2}", name, rule.Head.Arguments[i].Value, type);
}
}
}
private static Functor GetFunctor(string name, IFunctorProvider provider, Module from)
{
Functor result;
if (provider != null)
{
result = provider.FindFunctor(name, from);
if (result != null)
{
return result;
}
}
result = PredicateType.GetBuiltInType(name);
if (result != null)
{
return result;
}
if (name == Functor.ClassObject.Name)
{
return Functor.ClassObject;
}
if (SystemModule != null)
{
result = SystemModule.GetPredicateType(name);
if (result != null)
{
return result;
}
}
GuanFunc func = GuanExpression.GetGuanFunc(name);
if (func != null)
{
return new EvaluatedFunctor(func);
}
return null;
}
private static PredicateType GetGoalPredicateType(string typeName, IFunctorProvider provider, Module from)
{
Functor functor = GetFunctor(typeName, provider, from);
EvaluatedFunctor evaluatedFunctor = functor as EvaluatedFunctor;
if (evaluatedFunctor != null)
{
return evaluatedFunctor.ConstraintType;
}
else
{
return functor as PredicateType;
}
}
private static void UpdateFunctor(CompoundTerm term, Dictionary<string, PredicateType> types, IFunctorProvider provider, Module from)
{
foreach (var arg in term.Arguments)
{
CompoundTerm compound = arg.Value as CompoundTerm;
if (compound != null)
{
if (!(compound.Functor is PredicateType))
{
PredicateType type;
if (types.TryGetValue(compound.Functor.Name, out type))
{
compound.Functor = type;
}
else
{
Functor functor = GetFunctor(compound.Functor.Name, provider, from);
if (functor != null)
{
compound.Functor = functor;
}
}
}
UpdateFunctor(compound, types, provider, from);
}
}
}
private static Module CreateSystemModule()
{
List<string> rules = new List<string>
{
"append([], ?Ys, ?Ys)",
"append([?X|?Xs], ?Ys, [?X|?Zs]) :- append(?Xs, ?Ys, ?Zs)",
"member(?X, [?X|_]",
"member(?X, [_|?Xs]) :- member(?X, ?Xs)",
"length([], 0)",
"length([_|?Xs], ?Y) :- length(?Xs, ?Z), ?Y = ?Z + 1",
"reverse(?Xs, ?Ys) :- _reverse(?Xs, [], ?Ys)",
"_reverse([], ?Ys, ?Ys",
"_reverse([?X|?Xs], ?A, ?Ys) :- _reverse(?Xs, [?X|?A], ?Ys)",
"AddToList(?X, [], [?X])",
"AddToList(?X, [?X|?Ys], [?X|?Ys]) :- !",
"AddToList(?X, [?Y|?Ys], [?X,?Y|?Ys]) :- ?X < ?Y, !",
"AddToList(?X, [?Y|?Ys], [?Y|?Zs]) :- AddToList(?X, ?Ys, ?Zs)",
"AddToMap(kv(?K,?V), [], [kv(?K,?V)])",
"AddToMap(kv(?K,?V), [kv(?K,_)|?Ys], [kv(?K,?V)|?Ys]) :- !",
"AddToMap(kv(?K,?V), [kv(?K1,V1)|?Ys], [kv(?K,?V),kv(?K1,V1)|?Ys]) :- ?K < ?K1, !",
"AddToMap(kv(?K,?V), [kv(?K1,V1)|?Ys], [kv(?K1,V1)|?Zs]) :- AddToMap(kv(?K,?V), ?Ys, ?Zs)"
};
return Module.Parse("System", rules, null);
}
}
}

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

@ -0,0 +1,127 @@
using System.Collections.Generic;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Class maintaining predicate types from multiple modules to provide
/// a lookup facility.
/// </summary>
internal class ModuleProvider : IFunctorProvider
{
/// <summary>
/// Predicate types with the same name but different modules.
/// </summary>
class PredicateFunctorList : List<PredicateType>
{
public PredicateType Find(Module from)
{
if (Count == 1)
{
return this[0];
}
List<PredicateType> result = new List<PredicateType>();
int score = -1;
foreach (PredicateType type in this)
{
int newScore = GetScore(type, from);
if (newScore > score)
{
result.Clear();
result.Add(type);
score = newScore;
}
else if (newScore == score)
{
result.Add(type);
}
}
if (result.Count == 0)
{
return null;
}
if (result.Count > 1)
{
throw new GuanException("Conflicting types {0} of module {1} and {2} referenced in {3}",
result[0].Name, result[0].Module, result[1].Module, from);
}
return result[0];
}
private int GetScore(PredicateType type, Module from)
{
string[] parts1 = type.Module.Name.Split('.');
string[] parts2 = from.Name.Split('.');
int i;
for (i = 0; i < parts1.Length && i < parts2.Length && parts1[i] == parts2[i]; i++) ;
return i;
}
}
private Dictionary<string, PredicateFunctorList> types_;
private List<IFunctorProvider> providers_;
public ModuleProvider()
{
types_ = new Dictionary<string, PredicateFunctorList>();
providers_ = new List<IFunctorProvider>();
}
public void Add(Module module)
{
foreach (PredicateType type in module.GetPublicTypes())
{
AddType(type);
}
}
public void Add(IFunctorProvider provider)
{
if (!providers_.Contains(provider))
{
providers_.Add(provider);
}
}
private void AddType(PredicateType type)
{
PredicateFunctorList entry;
if (!types_.TryGetValue(type.Name, out entry))
{
entry = new PredicateFunctorList();
types_.Add(type.Name, entry);
}
if (!entry.Contains(type))
{
entry.Add(type);
}
}
public Functor FindFunctor(string name, Module from)
{
PredicateFunctorList entry;
if (!types_.TryGetValue(name, out entry))
{
foreach (IFunctorProvider provider in providers_)
{
Functor result = provider.FindFunctor(name, from);
if (result != null)
{
return result;
}
}
return null;
}
return entry.Find(from);
}
}
}

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

@ -0,0 +1,66 @@
using System.Threading.Tasks;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Predicate type for "not".
/// </summary>
internal class NotPredicateType : PredicateType
{
class Resolver : PredicateResolver
{
public Resolver(CompoundTerm input, Constraint constraint, QueryContext context)
: base(input, constraint, context, 1)
{
}
public override async Task<UnificationResult> OnGetNextAsync()
{
CompoundTerm goal = (CompoundTerm)Input.Arguments[0].Value;
PredicateResolver resolver = goal.PredicateType.CreateResolver(goal, Constraint, Context);
UnificationResult result = await resolver.GetNextAsync();
if (result == null)
{
return UnificationResult.Empty;
}
return null;
}
}
public static readonly NotPredicateType Singleton = new NotPredicateType();
private static readonly EvaluatedFunctor NotConstraint = new EvaluatedFunctor(NotFunc.Singleton);
private NotPredicateType()
: base("not", true, 1, 1)
{
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
return new Resolver(input, constraint, context);
}
public override void AdjustTerm(CompoundTerm term, Rule rule)
{
base.AdjustTerm(term, rule);
if (!(term.Arguments[0].Value is CompoundTerm))
{
throw new GuanException("Invalid use of not predicate");
}
CompoundTerm goal = (CompoundTerm)term.Arguments[0].Value;
if (goal.Functor is EvaluatedFunctor)
{
term.Functor = NotConstraint.ConstraintType;
}
else if (!(goal.Functor is PredicateType))
{
throw new GuanException("Predicate type {0} in {1} is not defined", goal.Functor.Name, term);
}
}
}
}

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

@ -0,0 +1,63 @@
using System;
using System.Reflection;
using System.Globalization;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Adapater to expose object properties as a compund term using reflection.
/// </summary>
internal class ObjectCompundTerm : CompoundTerm
{
private object value_;
private Type type_;
private ObjectCompundTerm(object value)
: base(Functor.ClassObject)
{
value_ = value;
type_ = value.GetType();
}
public Type ObjectType
{
get
{
return type_;
}
}
public override Term GetArgument(string name)
{
if (type_.GetProperty(name) == null)
{
return null;
}
object result = type_.InvokeMember(name, BindingFlags.GetProperty, null, value_, null, CultureInfo.InvariantCulture);
GuanObject guanObject = result as GuanObject;
if (guanObject != null && guanObject.GetValue() != null)
{
result = guanObject.GetValue();
}
return Term.FromObject(result);
}
public static ObjectCompundTerm Create(object value)
{
if (value == null || value.GetType().IsPrimitive)
{
return null;
}
return new ObjectCompundTerm(value);
}
public override string ToString()
{
return value_.ToString();
}
}
}

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

@ -0,0 +1,95 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Guan.Logic
{
/// <summary>
/// Resolver that executes rules for the same predicate type in parallel.
/// </summary>
internal class ParallelRulePredicateResolver : PredicateResolver
{
private Module module_;
private List<Rule> rules_;
private List<RulePredicateResolver> resolvers_;
private List<Task<UnificationResult>> parallelTasks_;
public ParallelRulePredicateResolver(Module module, List<Rule> rules, CompoundTerm input, Constraint constraint, QueryContext context)
: base(input, constraint, context)
{
module_ = module;
rules_ = rules;
}
public override async Task<UnificationResult> OnGetNextAsync()
{
Context.IsStable = false;
// Initialize resolvers, contexts and tasks.
if (resolvers_ == null)
{
resolvers_ = new List<RulePredicateResolver>(rules_.Count);
parallelTasks_ = new List<Task<UnificationResult>>(rules_.Count);
foreach (Rule rule in rules_)
{
List<Rule> rules = new List<Rule>
{
rule
};
RulePredicateResolver resolver = new RulePredicateResolver(module_, rules, false, Input, Constraint, Context.CreateChild());
resolvers_.Add(resolver);
parallelTasks_.Add(resolver.GetNextAsync());
}
}
for (int i = 0; i < resolvers_.Count; i++)
{
if (parallelTasks_[i] != null)
{
resolvers_[i].Context.IsSuspended = false;
}
else
{
parallelTasks_[i] = resolvers_[i].GetNextAsync();
}
}
Context.IsStable = true;
UnificationResult result = null;
while (parallelTasks_.Count > 0 && result == null)
{
Task<UnificationResult> task = await Task.WhenAny(parallelTasks_);
result = task.Result;
int index = parallelTasks_.IndexOf(task);
if (result != null)
{
parallelTasks_[index] = null;
// Suspend all unfinished ones
for (int i = 0; i < resolvers_.Count; i++)
{
if (i != index)
{
resolvers_[i].Context.IsSuspended = true;
}
}
}
else
{
parallelTasks_.RemoveAt(index);
resolvers_.RemoveAt(index);
}
}
return result;
}
protected override void OnCancel()
{
foreach (RulePredicateResolver resolver in resolvers_)
{
resolver.Context.IsCancelled = true;
}
}
}
}

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

@ -0,0 +1,242 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Base class for resolving a goal.
/// </summary>
public abstract class PredicateResolver
{
private CompoundTerm input_;
private Constraint constraint_;
private QueryContext context_;
private int max_;
private int iteration_;
private bool completed_;
private static bool EnableTrace = Utility.GetConfig("EnableTrace", false);
public PredicateResolver(CompoundTerm input, Constraint constraint, QueryContext context, int max = int.MaxValue)
{
input_ = input;
constraint_ = constraint;
context_ = context;
max_ = max;
iteration_ = 0;
completed_ = false;
if (input != null && input.Option.Max != 0)
{
max_ = input.Option.Max;
}
}
public CompoundTerm Input
{
get
{
return input_;
}
internal set
{
input_ = value;
}
}
internal Constraint Constraint
{
get
{
return constraint_;
}
set
{
constraint_ = value;
}
}
public QueryContext Context
{
get
{
return context_;
}
}
public int Iteration
{
get
{
return iteration_;
}
internal set
{
iteration_ = value;
}
}
internal int Max
{
get
{
return max_;
}
set
{
max_ = value;
}
}
public bool Completed
{
get
{
return completed_ || context_.IsCancelled;
}
}
protected void Complete()
{
completed_ = true;
}
public async Task<UnificationResult> GetNextAsync()
{
if (completed_)
{
return null;
}
UnificationResult result = await OnGetNextAsync();
if (result != null)
{
iteration_++;
if (iteration_ >= max_)
{
completed_ = true;
}
}
else
{
completed_ = true;
}
return result;
}
public object GetBoundValue(string name, bool remove)
{
List<object> result = GetBoundValues(name, remove);
if (result == null)
{
return null;
}
ReleaseAssert.IsTrue(result.Count == 1);
return result[0];
}
public List<object> GetBoundValues(string name, bool remove)
{
Term term = GetTerm(name);
if (term == null)
{
return null;
}
if (term.IsGround())
{
List<object> result = new List<object>(1)
{
term.GetValue()
};
return result;
}
Variable variable = term as Variable;
if (variable == null)
{
return null;
}
return constraint_.GetValues(variable, remove);
}
public object GetLowerBound(string name, bool remove, out bool isInclusive)
{
isInclusive = false;
Variable variable = GetVariable(name);
if (variable == null)
{
return null;
}
return constraint_.GetLowerBound(variable, remove, out isInclusive);
}
public object GetUpperBound(string name, bool remove, out bool isInclusive)
{
isInclusive = false;
Variable variable = GetVariable(name);
if (variable == null)
{
return null;
}
return constraint_.GetUpperBound(variable, remove, out isInclusive);
}
private Term GetTerm(string name)
{
Term arg = Input.GetArgument(name);
if (arg == null)
{
return null;
}
return arg.GetEffectiveTerm();
}
private Variable GetVariable(string name)
{
Term term = GetTerm(name);
if (term == null)
{
return null;
}
return term as Variable;
}
public void Cancel()
{
if (!completed_)
{
OnCancel();
completed_ = true;
}
}
protected virtual void OnCancel()
{
}
public virtual void OnBacktrack()
{
}
public abstract Task<UnificationResult> OnGetNextAsync();
internal void WriteTrace(string type)
{
if (Input != null && !(Input.Functor is ConstraintPredicateType) && (EnableTrace || Context.EnableTrace))
{
EventLog.WriteInfo("Trace", "{0}\t[{1},{2}]: {3}", type, Input.Binding.Level, iteration_, Input);
}
}
}
}

221
Guan/Logic/PredicateType.cs Normal file
Просмотреть файл

@ -0,0 +1,221 @@
using System.Collections.Generic;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Functor for a predicate goal.
/// </summary>
public class PredicateType : Functor
{
private Module module_;
private bool isPublic_;
private int minArgument_;
private int maxArgument_;
private List<Rule> rules_;
private bool parallel_;
private bool singleActivation_;
private static Dictionary<string, PredicateType> BuiltInTypes = null;
private static Dictionary<string, PredicateType> CreateBuiltInTypes()
{
Dictionary<string, PredicateType> result = new Dictionary<string, PredicateType>();
AddType(result, CutPredicateType.Singleton);
AddType(result, ForwardCutPredicateType.Singleton);
AddType(result, FailPredicateType.Singleton);
AddType(result, UnifyPredicateType.Regular);
AddType(result, NotPredicateType.Singleton);
AddType(result, TermPropertyPredicateType.Var);
AddType(result, TermPropertyPredicateType.NonVar);
AddType(result, TermPropertyPredicateType.Atom);
AddType(result, TermPropertyPredicateType.Compound);
AddType(result, TermPropertyPredicateType.Ground);
AddType(result, TracePredicateType.Enable);
AddType(result, TracePredicateType.Disable);
AddType(result, GetValPredicateType.Singleton);
AddType(result, SetValPredicateType.Backtrack);
AddType(result, SetValPredicateType.NoBacktrack);
AddType(result, EnumerablePredicateType.Singleton);
AddType(result, AssertPredicateType.Assert);
AddType(result, AssertPredicateType.Asserta);
AddType(result, AssertPredicateType.Assertz);
AddType(result, UpdateObjectPredicateType.Singleton);
AddType(result, IsPredicateType.Singleton);
AddType(result, WriteLinePredicateType.Singleton);
return result;
}
private static void AddType(Dictionary<string, PredicateType> result, PredicateType type)
{
result.Add(type.Name, type);
}
public PredicateType(string name, bool isPublic = false, int minArgument = 0, int maxArgument = int.MaxValue)
: base(name)
{
isPublic_ = isPublic;
minArgument_ = minArgument;
maxArgument_ = maxArgument;
parallel_ = false;
singleActivation_ = false;
}
public bool IsPublic
{
get
{
return isPublic_;
}
}
public Module Module
{
get
{
return module_;
}
}
internal List<Rule> Rules
{
get
{
return rules_;
}
}
internal bool AllowPositionalArgument
{
get
{
return minArgument_ >= 0;
}
}
internal void LoadRules(List<string> rules, IFunctorProvider provider)
{
if (rules.Count == 0)
{
return;
}
List<string> publicTypes = new List<string>
{
Name
};
Module module = Module.Parse(Name, rules, provider, publicTypes);
ReleaseAssert.IsTrue(module_ == null || module_ == module);
module_ = module;
}
internal void AddRule(Module module, Rule rule, bool append)
{
if (module_ != null && module_ != module)
{
throw new GuanException("Rules for {0} set from both modules {1} and {2}", this, module_, module);
}
module_ = module;
if (rules_ == null)
{
rules_ = new List<Rule>();
}
if (append)
{
rules_.Add(rule);
}
else
{
rules_.Insert(0, rule);
}
}
public virtual PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
if (rules_ == null)
{
return null;
}
if (parallel_)
{
return new ParallelRulePredicateResolver(module_, rules_, input, constraint, context);
}
return new RulePredicateResolver(module_, rules_, singleActivation_, input, constraint, context);
}
public virtual void AdjustTerm(CompoundTerm term, Rule rule)
{
if (minArgument_ >= 0 && term.Arguments.Count < minArgument_)
{
throw new GuanException("Predicate must have at least {0} argument(s)", minArgument_);
}
if (maxArgument_ >= 0 && term.Arguments.Count > maxArgument_)
{
throw new GuanException("Predicate can have at most {0} argument(s)", maxArgument_);
}
}
internal void ProcessMetaData(string name, Term term)
{
if (name == null && term.GetStringValue() == "parallel")
{
parallel_ = true;
}
else if (name == null && term.GetStringValue() == "singleActivation")
{
singleActivation_ = true;
}
}
public static PredicateType GetBuiltInType(string name)
{
if (BuiltInTypes == null)
{
lock (typeof(PredicateType))
{
if (BuiltInTypes == null)
{
BuiltInTypes = CreateBuiltInTypes();
}
}
}
PredicateType result;
BuiltInTypes.TryGetValue(name, out result);
return result;
}
}
/// <summary>
/// When a predicate type is marked as dynamic, when resolving the goal
/// we will only look for asserted predicates.
/// This is different from standard Prolog where the predicates are also
/// getting resolved in standard ways.
/// </summary>
internal class DynamicPredicateType : PredicateType
{
public DynamicPredicateType(string name)
: base(name)
{
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
PredicateType assertedType = context.GetAssertedPredicateType(Name);
if (assertedType == null)
{
assertedType = FailPredicateType.Singleton;
}
return assertedType.CreateResolver(input, constraint, context);
}
}
}

182
Guan/Logic/Query.cs Normal file
Просмотреть файл

@ -0,0 +1,182 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Class for a query on some pre-defined predicate type.
/// </summary>
public class Query
{
class QueryPredicateType : PredicateType
{
private CompoundTerm input_;
public QueryPredicateType()
: base(QueryTypeName)
{
}
public CompoundTerm Input
{
get
{
return input_;
}
}
public override void AdjustTerm(CompoundTerm term, Rule rule)
{
ReleaseAssert.IsTrue(term == rule.Head && input_ == null);
foreach (string name in rule.VariableTable)
{
rule.AddArgument(term, "?" + name, name);
}
VariableBinding binding = rule.CreateBinding(0);
input_ = term.DuplicateGoal(binding);
}
public override string ToString()
{
return string.Empty;
}
}
class Provider : IFunctorProvider
{
private IFunctorProvider provider_;
private QueryPredicateType type_;
public Provider(IFunctorProvider provider, QueryPredicateType type)
{
provider_ = provider;
type_ = type;
}
public Functor FindFunctor(string name, Module from)
{
if (name == QueryTypeName)
{
return type_;
}
if (provider_ != null)
{
return provider_.FindFunctor(name, from);
}
return null;
}
}
private CompoundTerm input_;
private PredicateResolver resolver_;
private const string QueryTypeName = "__query";
private Query(QueryPredicateType queryType, QueryContext queryContext)
{
input_ = queryType.Input;
input_.Binding.MoveNext();
resolver_ = queryType.CreateResolver(input_, Constraint.Empty, queryContext);
}
public async Task<List<Term>> GetResultsAsync(int maxCount)
{
List<Term> result = new List<Term>();
while (result.Count < maxCount)
{
Term term = await GetNextAsync();
if (term == null)
{
return result;
}
result.Add(term.GetGroundedCopy());
}
return result;
}
public async Task<Term> GetNextAsync()
{
if (resolver_.Iteration > 0)
{
input_.Binding.MovePrev();
}
UnificationResult result = await resolver_.GetNextAsync();
if (result == null)
{
return null;
}
result.Apply(input_.Binding);
input_.Binding.MoveNext();
return input_;
}
public static Query Create(string text, QueryContext queryContext, IFunctorProvider provider)
{
if (text.Contains(":-"))
{
throw new ArgumentException("Query can't contain head: " + text);
}
QueryPredicateType queryType = new QueryPredicateType();
List<string> types = new List<string>
{
QueryTypeName
};
List<string> rules = new List<string>
{
QueryTypeName + " :- " + text
};
_ = Module.Parse("query", rules, new Provider(provider, queryType), types);
return new Query(queryType, queryContext);
}
public static Query Create(List<CompoundTerm> terms, QueryContext queryContext, IFunctorProvider provider)
{
if (terms == null || terms.Count == 0)
{
throw new ArgumentException("terms");
}
CompoundTerm body = terms[terms.Count - 1];
for (int i = terms.Count - 2; i >= 0; i--)
{
CompoundTerm current = body;
body = new CompoundTerm(",");
body.AddArgument(terms[i], "0");
body.AddArgument(current, "1");
}
QueryPredicateType queryType = new QueryPredicateType();
CompoundTerm ruleTerm = new CompoundTerm(":-");
ruleTerm.AddArgument(new CompoundTerm(QueryTypeName), "0");
ruleTerm.AddArgument(body, "1");
List<string> types = new List<string>
{
QueryTypeName
};
List<Rule> rules = new List<Rule>
{
Rule.Parse(ruleTerm, ruleTerm.ToString())
};
_ = Module.Parse("query", rules, new Provider(provider, queryType), types);
return new Query(queryType, queryContext);
}
}
}

349
Guan/Logic/QueryContext.cs Normal file
Просмотреть файл

@ -0,0 +1,349 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Context to provide the following during rule execution:
/// 1. The asserted predicates.
/// 2. Global variables, including some system variables controlling:
/// 2.1: resolve direction
/// 2.2: trace/debug flag
/// 3. A hierarhy of contexts to track parallel execution.
/// </summary>
public class QueryContext : IPropertyContext, IWritablePropertyContext
{
private QueryContext parent_;
private List<QueryContext> children_;
private QueryContext root_;
private string orderProperty_;
private ResolveOrder order_;
private Dictionary<string, object> variables_;
private Module asserted_;
private bool isLocalStable_;
private bool isStable_;
private bool isLocalSuspended_;
private bool isSuspended_;
private bool isCancelled_;
private bool enableTrace_;
private bool enableDebug_;
private List<IWaitingTask> waiting_;
private long seq_;
public event EventHandler Suspended;
public event EventHandler Resumed;
private static long Seq = 0;
public QueryContext()
{
seq_ = Interlocked.Increment(ref Seq);
root_ = this;
order_ = ResolveOrder.None;
variables_ = new Dictionary<string, object>();
asserted_ = new Module("asserted");
isStable_ = isLocalStable_ = false;
isSuspended_ = isLocalSuspended_ = false;
isCancelled_ = false;
enableTrace_ = false;
enableDebug_ = false;
waiting_ = new List<IWaitingTask>();
}
protected QueryContext(QueryContext parent)
{
seq_ = Interlocked.Increment(ref Seq);
parent_ = parent;
root_ = parent.root_;
orderProperty_ = parent.orderProperty_;
asserted_ = parent.asserted_;
order_ = parent.order_;
variables_ = parent.variables_;
isStable_ = isLocalStable_ = false;
isSuspended_ = isLocalSuspended_ = false;
isCancelled_ = false;
enableTrace_ = parent.enableTrace_;
enableDebug_ = parent.enableDebug_;
waiting_ = parent.waiting_;
lock (root_)
{
SetStable(false);
parent_.AddChild(this);
}
}
internal virtual QueryContext CreateChild()
{
return new QueryContext(this);
}
private void AddChild(QueryContext child)
{
if (children_ == null)
{
children_ = new List<QueryContext>();
}
children_.Add(child);
}
internal void ClearChildren()
{
lock (root_)
{
if (children_ != null)
{
children_.Clear();
}
isStable_ = isLocalStable_;
}
}
public void SetDirection(string directionProperty, ResolveOrder direction)
{
orderProperty_ = directionProperty;
order_ = direction;
}
public virtual object this[string name]
{
get
{
if (name == "Order")
{
return order_;
}
object result;
variables_.TryGetValue(name, out result);
return result;
}
set
{
if (name == "Order")
{
if (value is ResolveOrder)
{
order_ = (ResolveOrder)value;
}
else
{
order_ = (ResolveOrder)Enum.Parse(typeof(ResolveOrder), (string)value);
}
}
else
{
variables_[name] = value;
}
}
}
public string OrderProperty
{
get
{
return orderProperty_;
}
}
public ResolveOrder Order
{
get
{
return order_;
}
}
/// <summary>
/// Used to coordinate parallel execution where some tasks should be run
/// together.
/// A context is stable if the context itself and all its children contexts
/// are stable.
/// There is an execution queue at the root level for all tasks that should be
/// executed together and they will only be executed when the root context
/// is stable, which should only be true when no more such tasks will be
/// generated (until some such task is completed and new task is triggered).
/// </summary>
internal bool IsStable
{
get
{
return isStable_;
}
set
{
lock (root_)
{
SetStable(value);
}
}
}
/// <summary>
/// Whether the context is suspended, which can happen during parallel
/// execution.
/// A context is suspended when either itself or any parent is suspended.
/// </summary>
internal bool IsSuspended
{
get
{
return isSuspended_;
}
set
{
isLocalSuspended_ = value;
UpdateSuspended(parent_ != null ? parent_.isSuspended_ : false);
}
}
internal bool EnableTrace
{
get
{
return enableTrace_;
}
set
{
enableTrace_ = value;
}
}
internal bool EnableDebug
{
get
{
return enableDebug_ || enableTrace_;
}
set
{
enableDebug_ = value;
}
}
public bool IsCancelled
{
get
{
return isCancelled_;
}
set
{
Queue<QueryContext> queue = new Queue<QueryContext>();
queue.Enqueue(this);
while (queue.Count > 0)
{
QueryContext context = queue.Dequeue();
if (!context.IsCancelled)
{
context.isCancelled_ = true;
context.isSuspended_ = true;
if (context.children_ != null)
{
foreach (QueryContext child in context.children_)
{
queue.Enqueue(child);
}
}
}
}
}
}
private void SetStable(bool value)
{
isLocalStable_ = value;
QueryContext context = this;
bool updated;
do
{
updated = context.UpdateStable();
context = context.parent_;
} while (context != null && updated);
if (root_.isStable_)
{
root_.Start();
}
}
private bool UpdateStable()
{
bool result = isLocalStable_;
for (int i = 0; result && children_ != null && i < children_.Count; i++)
{
result = children_[i].isStable_;
}
if (result == isStable_)
{
return false;
}
isStable_ = result;
return true;
}
private void UpdateSuspended(bool value)
{
bool newValue = (value || isLocalSuspended_);
if (newValue == isSuspended_)
{
return;
}
isSuspended_ = newValue;
if (children_ != null)
{
foreach (QueryContext child in children_)
{
child.UpdateSuspended(isSuspended_);
}
}
if (isSuspended_)
{
Suspended?.Invoke(this, EventArgs.Empty);
}
else
{
Resumed?.Invoke(this, EventArgs.Empty);
}
}
internal void Assert(CompoundTerm term, bool append)
{
asserted_.Add(term, append);
}
internal PredicateType GetAssertedPredicateType(string name)
{
return asserted_.GetPredicateType(name);
}
internal void AddWaiting(IWaitingTask suspended)
{
waiting_.Add(suspended);
}
private void Start()
{
foreach (IWaitingTask suspended in waiting_)
{
Task.Run(() => { suspended.Start(); });
}
waiting_.Clear();
}
public override string ToString()
{
return seq_.ToString();
}
}
}

252
Guan/Logic/Rule.cs Normal file
Просмотреть файл

@ -0,0 +1,252 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Rule which contains a head and an optional body.
/// Parsing of a rule goes through the following steps:
/// 1. Parse the TermExpression into compound term.
/// 2. Break the compound term into head and bodies.
/// 3. For head and each goal, resolve the corresponding predicate type
/// 4. For every compound term, resolve the corresponding functor if there is one
/// 5. Invoke PostProcessing on each head & goal, which includes handling
/// argument name and variables, plus type-specific posting processing.
/// </summary>
public class Rule
{
private string text_;
private CompoundTerm head_;
private List<CompoundTerm> goals_;
private VariableTable variableTable_;
private static readonly Regex VariablePattern = new Regex(@"^\?[_\w]+$", RegexOptions.Compiled);
private static readonly Regex ArgumentNamePattern = new Regex(@"^[_\w]+$", RegexOptions.Compiled);
internal Rule(string text, CompoundTerm head, List<CompoundTerm> goals, VariableTable variableTable)
{
text_ = text;
head_ = head;
goals_ = goals;
variableTable_ = variableTable;
}
public CompoundTerm Head
{
get
{
return head_;
}
}
public List<CompoundTerm> Goals
{
get
{
return goals_;
}
}
internal VariableTable VariableTable
{
get
{
return variableTable_;
}
}
public void AddArgument(CompoundTerm term, string argument, string name)
{
Term arg = TermExpression.Parse(argument);
term.AddArgument(arg, name);
ProcessCompoundTerm(term, variableTable_, 0);
}
internal void PostProcessing()
{
foreach (CompoundTerm goal in goals_)
{
ProcessCompoundTerm(goal, variableTable_, 0);
goal.PostProcessing(this);
}
ProcessCompoundTerm(head_, variableTable_, 0);
head_.PostProcessing(this);
}
public static Rule Parse(string text)
{
return Parse(ToGoal(TermExpression.Parse(text)), text);
}
internal static Rule Parse(CompoundTerm rule, string text)
{
CompoundTerm head = null;
List<CompoundTerm> goals = new List<CompoundTerm>();
if (rule != null)
{
if (rule.Functor.Name != ":-")
{
head = rule;
}
else if (rule.Arguments.Count == 2)
{
head = ToGoal(rule.Arguments[0].Value);
if (head == null)
{
throw new GuanException("Invalid head in rule {0}", text);
}
CompoundTerm body = ToGoal(rule.Arguments[1].Value);
if (body == null)
{
throw new GuanException("Invalid body in rule {0}", text);
}
ExpandBody(body, goals);
}
}
if (head == null)
{
throw new GuanException("{0} is not a rule", text);
}
return new Rule(text, head, goals, new VariableTable());
}
private static CompoundTerm ToGoal(Term term)
{
CompoundTerm result = term as CompoundTerm;
if (result != null)
{
return result;
}
string name = term.GetStringValue();
if (name == null)
{
return null;
}
return new CompoundTerm(Functor.Parse(name));
}
private static void ExpandBody(CompoundTerm body, List<CompoundTerm> goals)
{
while (body.Functor.Name == ",")
{
ReleaseAssert.IsTrue(body.Arguments.Count == 2);
CompoundTerm goal = ToGoal(body.Arguments[0].Value);
CompoundTerm rest = ToGoal(body.Arguments[1].Value);
if (goal == null || rest == null)
{
throw new GuanException("Invalid goal {0}", body);
}
goals.Add(goal);
body = rest;
}
goals.Add(body);
}
private static void ProcessCompoundTerm(CompoundTerm goal, VariableTable variableTable, int level)
{
if (level > 0 || goal.Functor.Name != "not")
{
level++;
}
PredicateType predicateType = goal.Functor as PredicateType;
bool positional = (predicateType == null || predicateType.AllowPositionalArgument);
for (int i = 0; i < goal.Arguments.Count; i++)
{
bool nameOverride = false;
CompoundTerm compound = goal.Arguments[i].Value as CompoundTerm;
if (level > 0 && compound != null && compound.Functor.Name == "=")
{
ReleaseAssert.IsTrue(compound.Arguments.Count == 2);
string name = compound.Arguments[0].Value.GetStringValue();
if (name == null || !ArgumentNamePattern.IsMatch(name))
{
throw new GuanException("Invalid argument name {0} for {1}", name, compound.Arguments[1].Value);
}
positional = false;
goal.Arguments[i] = new TermArgument(name, compound.Arguments[1].Value, goal.Functor.GetArgumentDescription(name));
compound = goal.Arguments[i].Value as CompoundTerm;
}
else if (!positional)
{
nameOverride = true;
}
if (compound != null)
{
if (nameOverride)
{
goal.Arguments[i] = new TermArgument(compound.Functor.Name, compound, goal.Functor.GetArgumentDescription(compound.Functor.Name));
}
ProcessCompoundTerm(compound, variableTable, level);
if (compound.Functor.Name == "[")
{
goal.Arguments[i].Value = ListTerm.Parse(compound);
}
}
else
{
string constantValue = goal.Arguments[i].Value.GetStringValue();
string variableName;
if (constantValue == "_")
{
variableName = constantValue;
}
else if (constantValue != null && VariablePattern.IsMatch(constantValue))
{
variableName = constantValue.Substring(1);
}
else
{
variableName = null;
}
if (variableName != null)
{
int variableIndex = variableTable.GetIndex(variableName, true);
IndexedVariable variable = new IndexedVariable(variableIndex, variableName);
if (nameOverride)
{
goal.Arguments[i] = new TermArgument(variableName, variable, goal.Functor.GetArgumentDescription(variableName));
}
else
{
goal.Arguments[i].Value = variable;
}
}
else if (nameOverride)
{
goal.Arguments[i] = new TermArgument(constantValue, Constant.Null, goal.Functor.GetArgumentDescription(constantValue));
}
}
}
}
internal VariableBinding CreateBinding(int level)
{
return new VariableBinding(variableTable_, goals_.Count, level);
}
public override string ToString()
{
return text_;
}
}
}

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

@ -0,0 +1,414 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Resolver using rules. This is typical case except for external predicates.
/// </summary>
internal class RulePredicateResolver : PredicateResolver
{
private Module module_;
private List<Rule> rules_;
private bool singleActivation_;
private Rule currentRule_;
private int ruleIndex_;
private VariableBinding binding_;
private VariableBinding tail_;
private PredicateResolver[] resolvers_;
private Constraint[] constraints_;
private List<Task<UnificationResult>> tasks_;
public RulePredicateResolver(Module module, List<Rule> rules, bool singleActivation, CompoundTerm input, Constraint constraint, QueryContext context)
: base(input, constraint, context)
{
module_ = module;
rules_ = rules;
singleActivation_ = singleActivation;
ruleIndex_ = 0;
}
public override async Task<UnificationResult> OnGetNextAsync()
{
if (binding_ != null)
{
// when tail optimization is effective, both binding need to backtrack.
if (tail_ != null)
{
tail_.MovePrev();
}
Backtrack();
}
while (ruleIndex_ < rules_.Count)
{
if (binding_ == null)
{
if (InitializeRule())
{
binding_.MoveNext();
}
else
{
ruleIndex_++;
}
}
else
{
int currentIndex = binding_.CurrentIndex;
if (currentIndex == currentRule_.Goals.Count)
{
UnificationResult result = binding_.CreateOutput();
// When tail optimization is effective, the output is a two-stage
// process: we use the current binding output to update tail_ and
// then the real output is from _tail.
if (tail_ != null)
{
result.Apply(tail_);
tail_.MoveNext();
WriteTrace("T-Exit");
result = tail_.CreateOutput();
}
return result;
}
if (resolvers_[currentIndex] == null)
{
resolvers_[currentIndex] = CreateResolver(currentIndex);
if (currentRule_.Goals[currentIndex].PredicateType is CutPredicateType)
{
for (int i = currentIndex - 1; i >= 0; i--)
{
resolvers_[i].Cancel();
}
}
if (currentIndex == currentRule_.Goals.Count - 1 && !Context.EnableDebug && OptimizeTail())
{
WriteTrace("T-Call");
continue;
}
}
resolvers_[currentIndex].WriteTrace("Call");
UnificationResult goalResult;
if (tasks_ == null || tasks_.Count == 0)
{
goalResult = await resolvers_[currentIndex].GetNextAsync();
}
else
{
// When backtracked to a goal (so that its iteration is not 0),
// if it is a goal immediately before a forward cut, the task for
// this goal is already initiated and in the tasks_ collection.
if ((currentIndex + 1 == currentRule_.Goals.Count) ||
(resolvers_[currentIndex].Iteration == 0) ||
!(currentRule_.Goals[currentIndex + 1].PredicateType is ForwardCutPredicateType))
{
tasks_.Add(resolvers_[currentIndex].GetNextAsync());
}
while (true)
{
Task<UnificationResult> completedTask = await Task.WhenAny(tasks_);
goalResult = completedTask.Result;
if (completedTask == tasks_[tasks_.Count - 1])
{
tasks_.RemoveAt(tasks_.Count - 1);
break;
}
else if (goalResult != null)
{
// If an earlier parallel task is completed, cancel all tasks after
// it.
int index = tasks_.IndexOf(completedTask);
int count = tasks_.Count - index;
for (; currentIndex > 0 && count > 1; currentIndex--)
{
resolvers_[currentIndex].Context.IsCancelled = true;
resolvers_[currentIndex] = null;
if (currentRule_.Goals[currentIndex].PredicateType is ForwardCutPredicateType)
{
count--;
}
}
ReleaseAssert.IsTrue(count == 1);
tasks_.RemoveRange(index, tasks_.Count - index);
binding_ = resolvers_[currentIndex].Input.Binding;
break;
}
else
{
tasks_.Remove(completedTask);
}
}
}
if (goalResult != null)
{
goalResult.Apply(binding_);
bool proceed = UpdateConstraints(goalResult);
binding_.MoveNext();
if (proceed)
{
resolvers_[currentIndex].WriteTrace("Exit");
}
else
{
Backtrack();
}
}
else if (currentRule_.Goals[currentIndex].PredicateType is ForwardCutPredicateType)
{
resolvers_[currentIndex] = null;
currentIndex--;
binding_ = resolvers_[currentIndex].Input.Binding;
resolvers_[currentIndex].Context.ClearChildren();
}
else
{
resolvers_[currentIndex].WriteTrace("Fail");
Backtrack();
}
}
}
return null;
}
private bool InitializeRule()
{
currentRule_ = rules_[ruleIndex_];
binding_ = currentRule_.CreateBinding(Input.Binding.Level + 1);
if (!binding_.Unify(currentRule_.Head.DuplicateGoal(binding_), Input))
{
binding_ = null;
return false;
}
resolvers_ = new PredicateResolver[currentRule_.Goals.Count];
constraints_ = new Constraint[currentRule_.Goals.Count];
if (currentRule_.Goals.Count > 0)
{
constraints_[0] = Constraint;
}
return true;
}
private PredicateResolver CreateResolver(int index)
{
CompoundTerm goal = currentRule_.Goals[index];
QueryContext context = null;
for (int i = index - 1; i >= 0 && context == null; i--)
{
context = resolvers_[i].Context;
}
if (context == null)
{
context = Context;
}
// For forwardcut, the context will be a child to the context for the goal
// immediately before it.
if (currentRule_.Goals[index].PredicateType is ForwardCutPredicateType && index > 0 && !resolvers_[index - 1].Completed)
{
if (tasks_ == null)
{
tasks_ = new List<Task<UnificationResult>>();
}
VariableBinding currentBinding = binding_;
binding_ = new VariableBinding(binding_);
currentBinding.MovePrev();
context = context.CreateChild();
tasks_.Add(resolvers_[index - 1].GetNextAsync());
}
PredicateResolver result = goal.PredicateType.CreateResolver(goal.DuplicateGoal(binding_), constraints_[index], context);
if (result == null)
{
throw new GuanException("No resolver defined for {0}", goal.PredicateType);
}
return result;
}
private bool UpdateConstraints(UnificationResult result)
{
int currentIndex = binding_.CurrentIndex;
Constraint current = constraints_[currentIndex];
Constraint next;
// The constraints propagated to the next goal comes from two sources:
// One is the constraints from the current goal (the ones remaining after
// applying the output from the current goal, second is the constraints
// added by the current goal.
if (currentIndex == currentRule_.Goals.Count - 1)
{
next = null;
}
else if (result.Constraints != null)
{
next = new Constraint();
next.Add(result.Constraints);
}
else
{
next = current;
}
// At this point, next is either the same as currennt, or contains nothing
// from current.
if (!result.IsEmpty)
{
for (int i = 0; i < current.Terms.Count; i++)
{
Constant evaluted = current.Terms[i].Evaluate(Context) as Constant;
if (evaluted == null)
{
if (next != current && next != null)
{
next.Add(current.Terms[i]);
}
}
else if (!evaluted.IsTrue())
{
return false;
}
else if (next == current)
{
// Add previous constraints that have verified to remain pending,
// which effectlively removes the current constraint
next = new Constraint();
for (int j = 0; j < i; j++)
{
next.Add(current.Terms[j]);
}
}
}
}
else if (next != current && next != null)
{
next.Add(current.Terms);
}
if (next != null)
{
constraints_[currentIndex + 1] = next;
}
return true;
}
private void Backtrack()
{
int currentIndex = binding_.CurrentIndex;
if (currentIndex < resolvers_.Length && resolvers_[currentIndex] != null)
{
resolvers_[currentIndex].OnBacktrack();
resolvers_[currentIndex] = null;
}
if (binding_.MovePrev())
{
if (currentRule_.Goals[currentIndex - 1].Functor is CutPredicateType)
{
ruleIndex_ = rules_.Count;
}
}
else if (singleActivation_ && Iteration > 0)
{
ruleIndex_ = rules_.Count;
}
else
{
ruleIndex_++;
binding_ = null;
}
}
private bool OptimizeTail()
{
int index = currentRule_.Goals.Count - 1;
RulePredicateResolver ruleResolver = resolvers_[index] as RulePredicateResolver;
// We can optimize if the goal is defined by rule and it does not require us
// to maintain different Max count.
if (ruleResolver == null || ruleResolver.Max != Max)
{
return false;
}
// TODO: allow optimization when there is constraint
if (constraints_[index].Terms.Count > 0)
{
return false;
}
// Find the previous incomplete goal or cut.
bool cut = false;
bool incomplete = false;
for (int i = index - 1; i >= 0 && !cut && !incomplete; i--)
{
if (currentRule_.Goals[i].Functor == CutPredicateType.Singleton)
{
cut = true;
}
else if (!resolvers_[i].Completed)
{
incomplete = true;
}
}
// Optimize if cut or this is the last goal for the predicate
bool result = (cut || (!incomplete && ruleIndex_ == rules_.Count - 1));
if (result)
{
UpdateTail(ruleResolver);
Load(ruleResolver);
}
return result;
}
private void UpdateTail(RulePredicateResolver resolver)
{
if (tail_ == null)
{
tail_ = binding_;
}
else
{
// Use whatever output we have currently to update _tail
UnificationResult result = binding_.CreateOutput();
result.Apply(tail_);
// The input to the next goal is for the current bining, we need t
// update so that it uses tail_.
resolver.Input.Migrate(tail_);
}
tail_.ResetTail();
binding_ = null;
}
private void Load(RulePredicateResolver resolver)
{
Input = resolver.Input;
Iteration = 0;
module_ = resolver.module_;
rules_ = resolver.rules_;
ruleIndex_ = 0;
}
}
}

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

@ -0,0 +1,73 @@
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Predicate type for setval.
/// </summary>
internal class SetValPredicateType : PredicateType
{
class Resolver : BooleanPredicateResolver
{
private SetValPredicateType type_;
private string name_;
private object oldValue_;
public Resolver(SetValPredicateType type, CompoundTerm input, Constraint constraint, QueryContext context)
: base(input, constraint, context)
{
type_ = type;
name_ = Input.Arguments[0].Value.GetStringValue();
}
protected override bool Check()
{
if (type_ == Backtrack)
{
oldValue_ = Context[name_];
}
Term term = Input.Arguments[1].Value.GetEffectiveTerm();
Constant constant = term as Constant;
if (constant == null)
{
throw new GuanException("The 2nd argument of {0} is not a constant: {1}", type_.Name, term);
}
Context[name_] = constant.Value;
return true;
}
public override void OnBacktrack()
{
if (type_ == Backtrack)
{
Context[name_] = oldValue_;
}
}
}
public static readonly SetValPredicateType Backtrack = new SetValPredicateType("setval");
public static readonly SetValPredicateType NoBacktrack = new SetValPredicateType("nb_setval");
private SetValPredicateType(string name)
: base(name, true, 2, 2)
{
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
return new Resolver(this, input, constraint, context);
}
public override void AdjustTerm(CompoundTerm term, Rule rule)
{
string name = term.Arguments[0].Value.GetStringValue();
if (name == null)
{
throw new GuanException("The first argument of getval must be string: {0}", term);
}
}
}
}

172
Guan/Logic/Term.cs Normal file
Просмотреть файл

@ -0,0 +1,172 @@
using System.Collections.Generic;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Base class for terms, including Constant, Variable and CompoundTerm.
/// </summary>
public abstract class Term
{
public Term()
{
}
internal virtual VariableBinding Binding
{
get
{
return VariableBinding.Ground;
}
}
public abstract bool IsGround();
public virtual Term GetEffectiveTerm()
{
return this;
}
public object GetValue()
{
ReleaseAssert.IsTrue(IsGround());
Constant constant = this as Constant;
if (constant != null)
{
return constant.Value;
}
return this;
}
public string GetStringValue()
{
Constant constant = this as Constant;
if (constant == null)
{
return null;
}
return constant.Value as string;
}
internal Term ForceEvaluate(QueryContext context)
{
Term result = GetEffectiveTerm();
ReleaseAssert.IsTrue(!(result is Variable), "{0} is not gounded", this);
CompoundTerm compound = result as CompoundTerm;
if (compound != null && compound.Functor is EvaluatedFunctor)
{
foreach (var arg in compound.Arguments)
{
arg.Value = arg.Value.ForceEvaluate(context);
}
return compound.Evaluate(context);
}
return result;
}
/// <summary>
/// Get a copy that gets rid of variables so that the value is
/// not affected by backtracking,
/// </summary>
/// <returns>Grounded copy of the term</returns>
internal Term GetGroundedCopy()
{
Term term = GetEffectiveTerm();
Constant constant = term as Constant;
if (constant != null)
{
return constant;
}
ReleaseAssert.IsTrue(!(term is Variable), "{0} is not gounded", this);
CompoundTerm compound = (CompoundTerm)term;
CompoundTerm result = new CompoundTerm(compound.Functor, null);
foreach (var arg in compound.Arguments)
{
result.AddArgument(arg.Value.GetGroundedCopy(), arg.Name);
}
return result;
}
internal Term UpdateBinding(Dictionary<Variable, Variable> mapping, VariableBinding binding)
{
Term term = GetEffectiveTerm();
Constant constant = term as Constant;
if (constant != null)
{
return constant;
}
Variable variable = term as Variable;
if (variable != null)
{
Variable mappedVariable;
if (mapping.TryGetValue(variable, out mappedVariable))
{
return mappedVariable;
}
return variable;
}
CompoundTerm compound = (CompoundTerm)term;
CompoundTerm result = null;
for (int i = 0; i < compound.Arguments.Count; i++)
{
Term newArg = compound.Arguments[i].Value.UpdateBinding(mapping, binding);
if (result == null && newArg != compound.Arguments[i].Value)
{
result = new CompoundTerm(compound.Functor, Binding);
for (int j = 0; j < i; j++)
{
result.AddArgument(compound.Arguments[j].Value, compound.Arguments[j].Name);
}
}
if (result != null)
{
result.AddArgument(newArg, compound.Arguments[i].Name);
}
}
if (result != null)
{
return result;
}
return this;
}
internal bool Unify(Term other)
{
VariableBinding binding = Binding;
if (binding == VariableBinding.Ground)
{
binding = other.Binding;
}
return binding.Unify(this, other);
}
internal static Term FromObject(object value)
{
Term term = value as Term;
if (term != null)
{
return term;
}
return new Constant(value);
}
}
}

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

@ -0,0 +1,52 @@
namespace Guan.Logic
{
/// <summary>
/// Argument of a compound term.
/// </summary>
public class TermArgument
{
private string name_;
private ArgumentDescription desc_;
private Term value_;
public TermArgument(string name, Term value, ArgumentDescription desc = null)
{
name_ = name;
value_ = value;
desc_ = desc;
}
public string Name
{
get
{
return name_;
}
}
public ArgumentDescription Description
{
get
{
return desc_;
}
}
public Term Value
{
get
{
return value_;
}
set
{
value_ = value;
}
}
public override string ToString()
{
return Name + ":" + value_.ToString();
}
}
}

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

@ -0,0 +1,393 @@
using System.Collections.Generic;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Parser for term expressions.s
/// </summary>
internal static class TermExpression
{
enum Associativity
{
Left,
Right,
None,
Open,
Close
}
class Operator
{
public string Name;
public int Priority;
public Associativity Associativity;
public OperatorFunctor Func;
private string display_;
public Operator(string name, int priority, Associativity associativity, string display)
{
Name = name;
Priority = priority;
Associativity = associativity;
Func = new OperatorFunctor(this);
display_ = display;
}
public CompoundTerm CreateTerm()
{
return new CompoundTerm(Func);
}
public override string ToString()
{
return display_;
}
}
class OperatorFunctor : Functor
{
public Operator Operator;
public OperatorFunctor(Operator op)
: base(op.Name)
{
Operator = op;
}
public override string ToString()
{
return Operator.ToString();
}
}
class OperatorTri : Tri<Operator>
{
public void Add(string name, int priority, Associativity associativity = Associativity.Left, string display = null)
{
Add(name, new Operator(name, priority, associativity, display ?? name));
}
}
private static readonly OperatorTri Operators = CreateOperators();
private static readonly Functor IdentityFunctor = new Functor("()");
private static readonly Operator CloseParentheses = Operators.Get(")");
private static OperatorTri CreateOperators()
{
OperatorTri result = new OperatorTri();
result.Add("(", 99, Associativity.Open, "()");
result.Add(")", 99, Associativity.Close);
result.Add("[", 99, Associativity.Open);
result.Add("]", 99, Associativity.Close);
result.Add(":-", 89, Associativity.None);
result.Add(";", 79, Associativity.Right);
result.Add("|", 79, Associativity.Right);
result.Add(",", 78, Associativity.Right, "','");
result.Add("=", 69, Associativity.None);
result.Add("is", 69, Associativity.None);
result.Add("||", 49);
result.Add("&&", 48);
result.Add("==", 39, Associativity.None);
result.Add("!=", 39, Associativity.None);
result.Add(">", 38, Associativity.None);
result.Add(">=", 38, Associativity.None);
result.Add("<", 38, Associativity.None);
result.Add("<=", 38, Associativity.None);
result.Add("+", 19);
result.Add("-", 19);
result.Add("*", 18);
result.Add("/", 18);
return result;
}
public static Term Parse(string text)
{
// Conceptually, last is the top of the stack. It is separated from
// the stack for convenient access since we often need to examine
// the top item.
Stack<CompoundTerm> pending = new Stack<CompoundTerm>();
CompoundTerm last = new CompoundTerm(IdentityFunctor);
Term term = null;
int i = 0;
while (i <= text.Length)
{
CompoundTerm current = null;
string token;
Operator op = Read(text, ref i, out token);
if (op != null)
{
if (op.Name == "(")
{
if (token != null)
{
current = new CompoundTerm(new Functor(token));
token = null;
}
else
{
current = new CompoundTerm(IdentityFunctor);
}
op = null;
}
else if (op.Associativity != Associativity.Close)
{
current = new CompoundTerm(op.Func);
if (op.Associativity == Associativity.Open)
{
op = null;
}
}
}
if (token != null)
{
if (term != null)
{
throw new GuanException("Not well formed at {0} {1}", term, token);
}
term = Constant.Parse(token);
}
while (op != null && CanPop(last.Functor, ref op))
{
if (term != null)
{
AddArgument(last, term);
}
// Normalize "(term)" to "term".
if (op == null && last.Functor == IdentityFunctor && last.Arguments.Count == 1)
{
CompoundTerm temp = last.Arguments[0].Value as CompoundTerm;
if (temp != null)
{
last = temp;
}
}
if (current != null && current.Functor.Name == "," && op == null)
{
current = null;
term = null;
}
else
{
term = last;
if (pending.Count > 0)
{
last = pending.Pop();
}
else
{
ReleaseAssert.IsTrue(i >= text.Length);
}
}
}
if (current != null)
{
if (term != null)
{
AddArgument(current, term);
term = null;
}
pending.Push(last);
last = current;
}
}
ReleaseAssert.IsTrue(pending.Count == 0);
last = term as CompoundTerm;
if (last != null && last.Functor == IdentityFunctor && last.Arguments.Count == 1)
{
term = last.Arguments[0].Value;
}
return term;
}
private static void AddArgument(CompoundTerm term, Term arg)
{
term.AddArgument(arg, term.Arguments.Count.ToString());
}
/// <summary>
/// Whether the stack should be popped.
/// </summary>
/// <param name="last">Functor of the top item on the (conceptual) stack.</param>
/// <param name="current">The current operator, will be set to null if it
/// is consumed by the last term.</param>
/// <returns>True if the stack should pop.</returns>
private static bool CanPop(Functor last, ref Operator current)
{
OperatorFunctor lastOp = last as OperatorFunctor;
if (current.Name == ")")
{
if (lastOp == null)
{
current = null;
}
return true;
}
if (current.Name == "]")
{
if (lastOp != null && lastOp.Name == "[")
{
current = null;
}
return true;
}
if (lastOp == null || lastOp.Operator.Name == "(")
{
if (current.Name != ",")
{
return false;
}
current = null;
return true;
}
if (lastOp.Operator.Priority < current.Priority)
{
return true;
}
if (lastOp.Operator.Priority > current.Priority)
{
return false;
}
if (current.Associativity == Associativity.Left && lastOp.Operator.Associativity != Associativity.Right)
{
return true;
}
if (current.Associativity == Associativity.Left || lastOp.Operator.Associativity != Associativity.Right)
{
throw new GuanException("Invalid combination of operators {0} and {1}", lastOp.Operator, current);
}
return false;
}
/// <summary>
/// Read the next operator.
/// </summary>
/// <param name="text">The entire expression.</param>
/// <param name="offset">Start index in the expression for reading.</param>
/// <param name="token">The token before the operator, if any.</param>
/// <returns>The next operator, null if a token followed by white space is read.</returns>
private static Operator Read(string text, ref int offset, out string token)
{
int start = offset;
while (start < text.Length && char.IsWhiteSpace(text[start]))
{
start++;
}
Operator op = null;
token = null;
offset = start;
while (offset < text.Length && !char.IsWhiteSpace(text[offset]) && op == null)
{
char c = text[offset];
if (c == '"' || c == '\'')
{
if (offset != start)
{
throw new GuanException("Invalid quote in term expression: {0}", text);
}
int end = SkipQuote(text, c, offset);
token = text.Substring(start + 1, end - start - 1);
offset = end + 1;
}
else
{
if (c == '_' || char.IsLetterOrDigit(c))
{
// check for operators like "is"
if (offset == start)
{
op = Operators.Get(text, ref offset);
// If followed by a non-symbol character, consider it as word prefix instead of operator
if (op != null && offset < text.Length && (text[offset] == '_' || char.IsLetterOrDigit(text[offset])))
{
op = null;
offset = start;
}
}
}
else
{
op = Operators.Get(text, ref offset);
}
if (op == null)
{
if (token != null)
{
throw new GuanException("Invalid quote in term expression: {0}", text);
}
offset++;
}
}
}
if (token == null)
{
int tokenEnd = (op != null ? offset - op.Name.Length : offset);
if (tokenEnd > start)
{
token = text.Substring(start, tokenEnd - start);
}
}
// When the end of expression is reached, add a ')' to balance to the initial
// '(' introduced when parsing is started.
if (op == null && offset == text.Length)
{
op = CloseParentheses;
offset++;
}
return op;
}
private static int SkipQuote(string text, char quote, int index)
{
int i = index;
while (++i < text.Length)
{
if (text[i] == quote)
{
return i;
}
else if (text[i] == '\\')
{
i++;
}
}
return index;
}
}
}

65
Guan/Logic/TermOption.cs Normal file
Просмотреть файл

@ -0,0 +1,65 @@
namespace Guan.Logic
{
/// <summary>
/// Execution option for goal term.
/// </summary>
public class TermOption
{
private int max_;
private CompoundTerm term_;
public static readonly string MaxIteration = "max";
public static readonly TermOption Default = new TermOption();
private static readonly Functor OptionFunctor = new Functor("_option");
public TermOption()
: this(new CompoundTerm(OptionFunctor))
{
}
public TermOption(CompoundTerm term)
{
term_ = term;
object maxValue = term_[MaxIteration];
if (maxValue != null)
{
max_ = (int)(long)maxValue;
term_.RemoveArgument(MaxIteration);
}
}
public int Max
{
get
{
return max_;
}
}
public object this[string name]
{
get
{
if (name == TermOption.MaxIteration)
{
return max_;
}
return term_[name];
}
internal set
{
if (name == TermOption.MaxIteration)
{
max_ = (int)value;
}
else
{
term_[name] = value;
}
}
}
}
}

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

@ -0,0 +1,60 @@
namespace Guan.Logic
{
/// <summary>
/// Predicate types for term inspection.
/// </summary>
internal class TermPropertyPredicateType : PredicateType
{
class Resolver : BooleanPredicateResolver
{
private TermPropertyPredicateType type_;
public Resolver(TermPropertyPredicateType type, CompoundTerm input, Constraint constraint, QueryContext context)
: base(input, constraint, context)
{
type_ = type;
}
protected override bool Check()
{
Term term = Input.Arguments[0].Value.GetEffectiveTerm();
if (type_ == Var)
{
return term is Variable;
}
else if (type_ == NonVar)
{
return !(term is Variable);
}
else if (type_ == Atom)
{
return term is Constant;
}
else if (type_ == Compound)
{
return term is CompoundTerm;
}
else
{
return term.IsGround();
}
}
}
public static readonly TermPropertyPredicateType Var = new TermPropertyPredicateType("var");
public static readonly TermPropertyPredicateType NonVar = new TermPropertyPredicateType("nonvar");
public static readonly TermPropertyPredicateType Atom = new TermPropertyPredicateType("atom");
public static readonly TermPropertyPredicateType Compound = new TermPropertyPredicateType("compound");
public static readonly TermPropertyPredicateType Ground = new TermPropertyPredicateType("ground");
private TermPropertyPredicateType(string name)
: base(name, true, 1, 1)
{
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
return new Resolver(this, input, constraint, context);
}
}
}

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

@ -0,0 +1,45 @@
namespace Guan.Logic
{
/// <summary>
/// Predicate type for trace.
/// </summary>
internal class TracePredicateType : PredicateType
{
class Resolver : BooleanPredicateResolver
{
private bool enable_;
private bool oldValue_;
public Resolver(bool enable, QueryContext context)
: base(null, null, context)
{
enable_ = enable;
}
protected override bool Check()
{
oldValue_ = Context.EnableTrace;
Context.EnableTrace = enable_;
return true;
}
public override void OnBacktrack()
{
Context.EnableTrace = oldValue_;
}
}
public static readonly TracePredicateType Enable = new TracePredicateType("trace");
public static readonly TracePredicateType Disable = new TracePredicateType("notrace");
private TracePredicateType(string name)
: base(name)
{
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
return new Resolver(this == Enable, context);
}
}
}

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

@ -0,0 +1,98 @@
using System.Collections.Generic;
using System.Text;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// The output from resolving a goal, including the instantiation of
/// variables and the constraints to be propagated to the remaining
/// goals.
/// </summary>
public class UnificationResult
{
private List<OutputVariable> entries_;
private List<CompoundTerm> constraints_;
internal static readonly UnificationResult Empty = new UnificationResult(0);
public UnificationResult(int capacity)
{
entries_ = new List<OutputVariable>(capacity);
}
internal void Add(OutputVariable entry)
{
entries_.Add(entry);
}
internal bool IsEmpty
{
get
{
return (entries_.Count == 0);
}
}
public List<CompoundTerm> Constraints
{
get
{
return constraints_;
}
}
public void AddConstraint(CompoundTerm constraint)
{
if (constraints_ == null)
{
constraints_ = new List<CompoundTerm>();
}
constraints_.Add(constraint);
}
internal void Apply(VariableBinding binding)
{
ReleaseAssert.IsTrue(binding.CurrentIndex >= 0);
foreach (var entry in entries_)
{
Term value = entry.GetEffectiveTerm();
if (!value.IsGround())
{
CompoundTerm compound = value as CompoundTerm;
if (compound != null)
{
value = compound.DuplicateOutput(binding);
}
else
{
OutputVariable outputVariable = (OutputVariable) value;
value = outputVariable.Original;
}
}
ReleaseAssert.IsTrue(entry.Original.Binding == binding);
entry.Original.SetValue(value);
}
}
public override string ToString()
{
StringBuilder result = new StringBuilder();
foreach (var entry in entries_)
{
Term value = entry.GetBoundTerm();
result.AppendFormat("?{0}={1},", entry.Original.Name, value);
}
if (result.Length > 1)
{
result.Length--;
}
return result.ToString();
}
}
}

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

@ -0,0 +1,36 @@
namespace Guan.Logic
{
/// <summary>
/// Predicate type for explicit unification.
/// </summary>
internal class UnifyPredicateType : PredicateType
{
class Resolver : BooleanPredicateResolver
{
public Resolver(CompoundTerm input, Constraint constraint, QueryContext context)
: base(input, constraint, context)
{
}
protected override bool Check()
{
Term term1 = Input.Arguments[0].Value.GetEffectiveTerm();
Term term2 = Input.Arguments[1].Value.GetEffectiveTerm();
return term1.Unify(term2);
}
}
public static readonly UnifyPredicateType Regular = new UnifyPredicateType("=");
private UnifyPredicateType(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,52 @@
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Predicate type for updateobj.
/// </summary>
internal class UpdateObjectPredicateType : PredicateType
{
class Resolver : BooleanPredicateResolver
{
public Resolver(CompoundTerm input, Constraint constraint, QueryContext context)
: base(input, constraint, context)
{
}
protected override bool Check()
{
Term term1 = Input.Arguments[0].Value.GetEffectiveTerm();
IWritablePropertyContext context = term1.GetValue() as IWritablePropertyContext;
if (context == null)
{
throw new GuanException("1st argument of updateobj {0} is not writable context", term1);
}
Constant term2 = Input.Arguments[1].Value.GetEffectiveTerm() as Constant;
string name = term2.GetStringValue();
if (name == null)
{
throw new GuanException("2nd argument of updateobj {0} is not a string", term2);
}
Term term3 = Input.Arguments[2].Value.GetEffectiveTerm();
context[name] = term3.GetValue();
return true;
}
}
public static readonly UpdateObjectPredicateType Singleton = new UpdateObjectPredicateType();
private UpdateObjectPredicateType()
: base("updateobj", true, 3, 3)
{
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
return new Resolver(input, constraint, context);
}
}
}

136
Guan/Logic/Variable.cs Normal file
Просмотреть файл

@ -0,0 +1,136 @@
using System.Collections.Generic;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Variable term at runtime which can be bound to another term, including
/// another variable.
/// We are not using log-based undo of the bound value during backtracking,
/// instead, the Variable object is coupled with the corresponding VaribleBinding
/// object to determine whether the bound value is still valid.
/// </summary>
public class Variable : Term
{
private string name_;
private Term value_;
private int index_;
private int sequence_;
private VariableBinding binding_;
internal Variable(string name, VariableBinding binding)
{
name_ = name;
binding_ = binding;
index_ = int.MaxValue;
sequence_ = -1;
}
internal Variable(Variable other, VariableBinding binding)
{
name_ = other.name_;
binding_ = binding;
index_ = other.index_;
sequence_ = other.sequence_;
if (other.binding_.IsValid(other.index_, other.sequence_))
{
value_ = other.value_;
}
}
internal void Reset()
{
index_ = int.MaxValue;
sequence_ = -1;
}
public string Name
{
get
{
return name_;
}
}
internal override VariableBinding Binding
{
get
{
return binding_;
}
}
public override bool IsGround()
{
Term value = GetBoundTerm();
return (value != null && value.IsGround());
}
public override Term GetEffectiveTerm()
{
Term result = GetBoundTerm();
if (result == null)
{
result = this;
}
else
{
result = result.GetEffectiveTerm();
}
return result;
}
internal Term GetBoundTerm()
{
Term result = null;
Variable current = this;
while (current != null && current.value_ != null && binding_.IsValid(current.index_, sequence_))
{
result = current.value_;
current = result as Variable;
}
return result;
}
public void SetValue(Term value)
{
ReleaseAssert.IsTrue(!binding_.IsValid(index_, sequence_));
index_ = binding_.CurrentIndex;
sequence_ = binding_.Sequence;
value_ = value;
}
internal void UpdateValueBinding(Dictionary<Variable, Variable> mapping, VariableBinding binding)
{
if (value_ != null)
{
value_ = value_.UpdateBinding(mapping, binding);
}
}
internal bool Promote()
{
if (binding_.IsValid(index_, sequence_))
{
index_ = -1;
return true;
}
return false;
}
public override string ToString()
{
Term value = GetEffectiveTerm();
if (value != this)
{
return value.ToString();
}
return "?" + name_;
}
}
}

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

@ -0,0 +1,439 @@
using System;
using System.Threading;
using System.Collections.Generic;
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// The collection of variables for a given rule at runtime.
/// </summary>
public class VariableBinding
{
/// <summary>
/// Local variables as defined in the rule.
/// </summary>
private Variable[] local_;
/// <summary>
/// Variables returned to the caller.
/// </summary>
private List<OutputVariable> output_;
/// <summary>
/// Variables returned from the goals.
/// </summary>
private List<LinkedVariable> foreign_;
/// <summary>
/// The start offset of foreign variable for each goal.
/// </summary>
private int[] offsets_;
private int currentIndex_;
private int[] sequences_;
private int count_;
private int level_;
private long seq_;
private static long Seq = 0;
internal static readonly VariableBinding Ground = new VariableBinding(new VariableTable(), 0, 0);
public VariableBinding(VariableTable variableTable, int goalCount, int level)
{
level_ = level;
seq_ = Interlocked.Increment(ref Seq);
local_ = new Variable[variableTable.Count];
for (int i = 0; i < variableTable.Count; i++)
{
local_[i] = new Variable(variableTable[i], this);
}
output_ = new List<OutputVariable>();
count_ = 0;
foreign_ = new List<LinkedVariable>(goalCount);
offsets_ = new int[goalCount];
sequences_ = new int[goalCount];
for (int i = 0; i < goalCount; i++)
{
offsets_[i] = 0;
sequences_[i] = 0;
}
currentIndex_ = -1;
}
public VariableBinding(VariableBinding other)
{
seq_ = Interlocked.Increment(ref Seq);
level_ = other.level_;
currentIndex_ = other.currentIndex_;
count_ = other.count_;
Dictionary<Variable, Variable> mapping = new Dictionary<Variable, Variable>();
local_ = new Variable[other.local_.Length];
for (int i = 0; i < local_.Length; i++)
{
local_[i] = new Variable(other.local_[i], this);
mapping.Add(other.local_[i], local_[i]);
}
output_ = new List<OutputVariable>(other.output_.Count);
foreach (OutputVariable outputVariable in other.output_)
{
output_.Add(new OutputVariable(outputVariable, this));
mapping.Add(outputVariable, output_[output_.Count - 1]);
}
foreign_ = new List<LinkedVariable>(other.count_);
for (int i = 0; i < other.count_; i++)
{
foreign_.Add(new LinkedVariable(other.foreign_[i], this));
mapping.Add(other.foreign_[i], foreign_[foreign_.Count - 1]);
}
offsets_ = new int[other.offsets_.Length];
sequences_ = new int[other.sequences_.Length];
for (int i = 0; i < other.offsets_.Length; i++)
{
offsets_[i] = other.offsets_[i];
sequences_[i] = other.sequences_[i];
}
foreach (Variable variable in local_)
{
variable.UpdateValueBinding(mapping, other);
}
foreach (Variable variable in output_)
{
variable.UpdateValueBinding(mapping, other);
}
foreach (Variable variable in foreign_)
{
variable.UpdateValueBinding(mapping, other);
}
}
internal int Level
{
get
{
return level_;
}
}
internal int CurrentIndex
{
get
{
return currentIndex_;
}
}
internal int Sequence
{
get
{
return (currentIndex_ < 0 ? 0 : sequences_[currentIndex_]);
}
}
internal bool IsValid(int index, int sequence)
{
return (index < 0 || (index <= currentIndex_ && sequences_[index] == sequence));
}
public Variable GetLocalVariable(int index)
{
return local_[index];
}
public OutputVariable AddOutputVariable(Variable original)
{
ReleaseAssert.IsTrue(currentIndex_ == -1);
foreach (OutputVariable existing in output_)
{
if (existing.Original == original)
{
return existing;
}
}
OutputVariable result = new OutputVariable(this, original, "$" + output_.Count.ToString());
output_.Add(result);
return result;
}
public LinkedVariable AddForeignVariable(Variable original)
{
LinkedVariable result;
for (int i = offsets_[currentIndex_]; i < count_; i++)
{
if (foreign_[i].Original == original)
{
return foreign_[i];
}
}
if (count_ < foreign_.Count)
{
result = foreign_[count_];
result.Original = original;
result.Reset();
}
else
{
result = new LinkedVariable(this, original, foreign_.Count.ToString() + "$");
foreign_.Add(result);
}
count_++;
return result;
}
public void MoveNext()
{
currentIndex_++;
if (currentIndex_ < offsets_.Length)
{
offsets_[currentIndex_] = count_;
sequences_[currentIndex_]++;
}
}
public bool MovePrev()
{
if (currentIndex_ == 0)
{
return false;
}
if (currentIndex_ < offsets_.Length)
{
count_ = offsets_[currentIndex_];
}
currentIndex_--;
sequences_[currentIndex_]++;
return true;
}
public UnificationResult CreateOutput()
{
if (output_.Count == 0)
{
return UnificationResult.Empty;
}
UnificationResult result = new UnificationResult(output_.Count);
foreach (OutputVariable entry in output_)
{
if (entry.GetBoundTerm() != null)
{
result.Add(entry);
}
}
return result;
}
public bool Unify(Term term1, Term term2)
{
term1 = term1.GetEffectiveTerm();
Variable var1 = term1 as Variable;
term2 = term2.GetEffectiveTerm();
Variable var2 = term2 as Variable;
if (var2 != null && var2.Binding != this)
{
// The output variable might already be bound, so refreshing the values
term2 = AddOutputVariable(var2).GetEffectiveTerm();
var2 = term2 as Variable;
}
if (term1 == term2)
{
return true;
}
if (var2 != null)
{
// If both are variables, bind input to local, or both are local binding
// anyone to the other is fine.
if (var1 != null && var2 is LinkedVariable)
{
var1.SetValue(term2);
}
else
{
var2.SetValue(term1);
}
return true;
}
CompoundTerm compound1;
CompoundTerm compound2;
if (var1 != null)
{
compound2 = term2 as CompoundTerm;
// If local is variable and the input is compound, we need to bind
// the local variable to the compound mapped to local binding.
if (compound2 != null && compound2.Binding != this)
{
term2 = compound2.DuplicateInput(this);
}
ReleaseAssert.IsTrue(var1 != term2);
var1.SetValue(term2);
return true;
}
// At this point neither is variable.
// In addition to unify constant/constant and compound/compound, we also try
// to unify constant(object)/compound, which can be useful for external predicates.
// compound/constant is not currently being unified until we find a use case for it.
Constant constant1 = term1 as Constant;
Constant constant2 = term2 as Constant;
if (constant1 != null)
{
// If both are constants, unify if the two are equal.
if (constant2 != null)
{
return object.Equals(constant1.Value, constant2.Value);
}
// Try to unify as compound
ObjectCompundTerm objectCompundTerm = ObjectCompundTerm.Create(constant1.Value);
if (objectCompundTerm == null)
{
return false;
}
compound2 = (CompoundTerm)term2;
if (compound2.Functor != Functor.ClassObject)
{
// Check whether class name (including base classes) matches input compound functor name
Type type = objectCompundTerm.ObjectType;
bool found = false;
while (!found)
{
if (type.Name == compound2.Functor.Name || type.FullName == compound2.Functor.Name)
{
found = true;
}
else
{
type = type.BaseType;
if (type == null)
{
return false;
}
}
}
}
compound1 = objectCompundTerm;
}
else
{
if (constant2 != null)
{
return false;
}
compound1 = (CompoundTerm)term1;
compound2 = (CompoundTerm)term2;
if (!compound1.Functor.Unify(compound2.Functor))
{
return false;
}
// If local contains effective type argument, unify it with the input functor name.
Term arg1 = compound1.GetEffetiveType();
if (arg1 != null && compound2.GetEffetiveType() == null && !Unify(arg1, new Constant(compound2.Functor.Name)))
{
return false;
}
// If local contains "this" argument, unify it with the entire input term.
arg1 = compound1.GetArgument("this");
if (arg1 != null && compound2.IsGround() && !Unify(arg1, compound2))
{
return false;
}
}
// Unify every argument of input
foreach (var arg2 in compound2.GetUnificationArgument())
{
// Only unify when an argument is present on both sides
Term arg1 = (arg2.Name == "this" ? compound1 : compound1.GetArgument(arg2.Name));
if (arg1 != null && !Unify(arg1, arg2.Value))
{
return false;
}
}
return true;
}
/// <summary>
/// Update the binding during tail optimization.
/// </summary>
public void ResetTail()
{
// These variables won't backtrack so assign -1 as their index.
foreach (var variable in local_)
{
variable.Promote();
}
foreach (var variable in output_)
{
variable.Promote();
}
// Retain the foreign variables that have not been instantiated. For those that
// have already been instantiated, we don't need to keep track of them explicitly
// in the binding: C# memory management can automatically takes care of them.
List<LinkedVariable> foreign = new List<LinkedVariable>(foreign_.Count);
for (int i = 0; i < count_; i++)
{
if (!foreign_[i].Promote())
{
foreign.Add(foreign_[i]);
}
}
foreign_ = foreign;
count_ = foreign_.Count;
currentIndex_ = 0;
}
public void ResetOutput()
{
output_.Clear();
}
public void ResetLast()
{
if (currentIndex_ >= 0)
{
sequences_[currentIndex_]++;
}
}
public override string ToString()
{
return seq_.ToString();
}
}
}

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

@ -0,0 +1,38 @@
using System.Collections.Generic;
namespace Guan.Logic
{
/// <summary>
/// Collection of variables in a rule.
/// </summary>
public class VariableTable : List<string>
{
public static readonly VariableTable Empty = new VariableTable();
public VariableTable()
{
}
public int GetIndex(string name, bool create)
{
if (name != "_")
{
for (int i = 0; i < Count; i++)
{
if (this[i] == name)
{
return i;
}
}
}
if (!create)
{
return -1;
}
Add(name);
return Count - 1;
}
}
}

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

@ -0,0 +1,44 @@
using Guan.Common;
namespace Guan.Logic
{
/// <summary>
/// Predicate type for writing a line to log.
/// </summary>
internal class WriteLinePredicateType : PredicateType
{
class Resolver : BooleanPredicateResolver
{
public Resolver(CompoundTerm input, Constraint constraint, QueryContext context)
: base(input, constraint, context)
{
}
protected override bool Check()
{
string format = Input.Arguments[0].Value.GetEffectiveTerm().GetStringValue();
object[] args = new object[Input.Arguments.Count - 1];
for (int i = 1; i < Input.Arguments.Count; i++)
{
args[i - 1] = Input.Arguments[i].Value.GetEffectiveTerm();
}
EventLog.WriteInfo("WriteLine", format, args);
return true;
}
}
public static readonly WriteLinePredicateType Singleton = new WriteLinePredicateType();
private WriteLinePredicateType()
: base("WriteLine", true, 1)
{
}
public override PredicateResolver CreateResolver(CompoundTerm input, Constraint constraint, QueryContext context)
{
return new Resolver(input, constraint, context);
}
}
}

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

@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Guan")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Guan")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("211ff6eb-91ce-4b89-9543-5304834066e3")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

9
Guan/ResolveOrder.cs Normal file
Просмотреть файл

@ -0,0 +1,9 @@
namespace Guan.Logic
{
public enum ResolveOrder
{
Asc,
Dsc,
None
}
}