First Push
This commit is contained in:
Родитель
cb4f307eb4
Коммит
b1b3cab2c1
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")]
|
|
@ -0,0 +1,9 @@
|
|||
namespace Guan.Logic
|
||||
{
|
||||
public enum ResolveOrder
|
||||
{
|
||||
Asc,
|
||||
Dsc,
|
||||
None
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче