0x0001F36D před 5 roky
rodič
revize
0c9f92479d

+ 0 - 6
Yuuna.sln

@@ -18,8 +18,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 		.gitignore = .gitignore
 	EndProjectSection
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yuuna.Faker", "src\Yuuna.Faker\Yuuna.Faker.csproj", "{4F6BFA3D-FC78-4087-BC09-E6E4D3F487EE}"
-EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -38,10 +36,6 @@ Global
 		{FCA784A1-546A-47E1-B5C1-3CF6A44FB3A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{FCA784A1-546A-47E1-B5C1-3CF6A44FB3A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{FCA784A1-546A-47E1-B5C1-3CF6A44FB3A7}.Release|Any CPU.Build.0 = Release|Any CPU
-		{4F6BFA3D-FC78-4087-BC09-E6E4D3F487EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{4F6BFA3D-FC78-4087-BC09-E6E4D3F487EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{4F6BFA3D-FC78-4087-BC09-E6E4D3F487EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{4F6BFA3D-FC78-4087-BC09-E6E4D3F487EE}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 4 - 12
src/Yuuna.Faker/Fake.cs → src/Yuuna.Contracts.Test/Fake.cs

@@ -1,7 +1,7 @@
 // Author: Orlys
 // Github: https://github.com/Orlys
 
-namespace TestPlugin
+namespace Yuuna.Contracts.Test
 {
     using System;
 
@@ -9,18 +9,10 @@ namespace TestPlugin
     using Yuuna.Contracts.Patterns;
     using Yuuna.Contracts.Modules;
     using Yuuna.Contracts.Semantics;
-    using Yuuna.Common.Utilities;
-    using Newtonsoft.Json;
+    using Yuuna.Common.Utilities; 
 
     public sealed class Fake : ModuleBase
-    {
-        public Fake()
-        {
-            this.ModuleName = JsonConvert.DeserializeAnonymousType(@"{ ""Name"": ""Fake模組"" }", new { Name = default(string) }).Name;
-        }
-
-        protected override string ModuleName { get; }
-
+    { 
         protected override void BuildPatterns(IGroupManager g, IPatternBuilder p)
         {
             g.Define("open").AppendOrCreate(new[] { "打開", "開" });
@@ -31,7 +23,7 @@ namespace TestPlugin
             p.Build(g["open"], g["door"]).OnInvoke(score =>
             {
                 var DOOR = new { IS_OPENED = new[] { true, false, true, false }.RandomTakeOne() };
-                Console.WriteLine("門的狀態是: " + (DOOR.IS_OPENED ? "開著的" : "關著的" ));
+                Console.WriteLine("門的狀態是: " + (DOOR.IS_OPENED ? "開著的" : "關著的"));
                 // 開門
                 if (!DOOR.IS_OPENED)
                     return (Moods.Happy, "已經開好門囉 <3");

+ 27 - 0
src/Yuuna.Contracts.Test/Yuuna.Contracts.Test.csproj

@@ -0,0 +1,27 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Library</OutputType>
+    <TargetFramework>netcoreapp3.0</TargetFramework>
+    <ApplicationIcon />
+    <StartupObject />
+  </PropertyGroup>
+
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
+    <OutputPath></OutputPath>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <Reference Include="Yuuna.Common">
+      <HintPath>..\Yuuna.Contracts\bin\Debug\netcoreapp3.0\Yuuna.Common.dll</HintPath>
+    </Reference>
+    <Reference Include="Yuuna.Contracts">
+      <HintPath>..\Yuuna.Contracts\bin\Debug\netcoreapp3.0\Yuuna.Contracts.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+
+  <Target Name="PostBuild" AfterTargets="PostBuildEvent">
+    <Exec Command="copy $(TargetPath) $(SolutionDir)..\Yuuna\bin\Debug\netcoreapp3.0\modules\Test" />
+  </Target>
+
+</Project>

+ 25 - 0
src/Yuuna.Contracts.Test/Yuuna.Contracts.Test.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29503.13
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yuuna.Contracts.Test", "Yuuna.Contracts.Test.csproj", "{4117CA8B-5669-4566-9833-B15CBA86D064}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{4117CA8B-5669-4566-9833-B15CBA86D064}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{4117CA8B-5669-4566-9833-B15CBA86D064}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{4117CA8B-5669-4566-9833-B15CBA86D064}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{4117CA8B-5669-4566-9833-B15CBA86D064}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {90CFF114-DFD3-4510-B846-971FAFB96FD5}
+	EndGlobalSection
+EndGlobal

+ 10 - 6
src/Yuuna.Contracts/Modules/ModuleBase.cs

@@ -31,15 +31,19 @@ namespace Yuuna.Contracts.Modules
         Initialized,
     }
 
-    public abstract class ModuleBase 
+    internal interface IModule
     {
-        private const BindingFlags FLAGS = (BindingFlags)52;
+        IPatternSet Patterns { get; }
+    }
+
+    public abstract class ModuleBase  
+    { 
 
         protected virtual string ModuleName { get; }
 
         private readonly PatternFactory _patternfactory;
         
-        internal IPatternSet Patterns => this._patternfactory;
+        internal IPatternSet  Patterns => this._patternfactory;
 
         public ModuleBase()
         {
@@ -56,7 +60,7 @@ namespace Yuuna.Contracts.Modules
             get
             {
                 var t = this.GetType();
-                var getMethod = t.GetProperty(nameof(this.ModuleName), FLAGS).GetGetMethod(true);
+                var getMethod = t.GetProperty(nameof(this.ModuleName),  (BindingFlags)52).GetGetMethod(true);
                 if (!getMethod.GetBaseDefinition().Equals(getMethod))
                 {
                     try
@@ -85,8 +89,8 @@ namespace Yuuna.Contracts.Modules
         /// <summary>
         /// 初始化模組
         /// </summary>
-        /// <param name="textSegmenter"></param>
-        /// <param name="groupManager"></param>
+        /// <param name="textSegmenter">分詞器</param>
+        /// <param name="groupManager">群組管理</param>
         internal void Initialize(ITextSegmenter textSegmenter, IGroupManager groupManager)
         {
             if (this.Status.Equals(ModuleStatus.Uninitialized))

+ 3 - 3
src/Yuuna.Contracts/Optimization/DefaultStrategy.cs

@@ -98,12 +98,12 @@ namespace Yuuna.Contracts.Optimization
 
         }
 
-        public Alternative FindBest(IEnumerable<ModuleBase> plugins, IImmutableList<string> feed)
+        public Alternative FindBest(IEnumerable<IPatternSet> patternSetCollection, IImmutableList<string> feed)
         {
             var list = ImmutableArray.CreateBuilder<Match>();
-            foreach (var plugin in plugins)
+            foreach (var pattern in patternSetCollection)
             {
-                foreach (var p in plugin.Patterns.ToImmutable())
+                foreach (var p in pattern.ToImmutable())
                 {
                     var match = this.CountPatternMatch(p, feed);  
                     list.Add(match);

+ 5 - 1
src/Yuuna.Contracts/Optimization/IStrategy.cs

@@ -7,8 +7,12 @@ namespace Yuuna.Contracts.Optimization
     using System.Collections.Generic;
     using System.Collections.Immutable;
     using Yuuna.Contracts.Modules;
+    using Yuuna.Contracts.Patterns;
+
     public interface IStrategy
     {
-        Alternative FindBest(IEnumerable<ModuleBase> plugins, IImmutableList<string> feed);
+   
+        Alternative FindBest(IEnumerable<IPatternSet> patternSetCollection, IImmutableList<string> feed);
+        //Alternative FindBest(IEnumerable<ModuleBase> plugins, IImmutableList<string> feed);
     }
 }

+ 7 - 0
src/Yuuna.Contracts/TextSegmention/ITextSegmenter.cs

@@ -34,5 +34,12 @@ namespace Yuuna.Contracts.TextSegmention
         /// </summary>
         /// <param name="manager">群組管理器。</param>
         void Load(IGroupManager manager);
+
+
+        /// <summary>
+        /// 從 <paramref name="manager"/> 移除字典。
+        /// </summary>
+        /// <param name="manager">群組管理器。</param>
+        void Unload(IGroupManager manager);
     }
 }

+ 0 - 23
src/Yuuna.Faker/Yuuna.Faker.csproj

@@ -1,23 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <OutputType>Library</OutputType>
-    <TargetFramework>netcoreapp3.0</TargetFramework>
-    <ApplicationIcon />
-    <StartupObject />
-    <RunPostBuildEvent>Always</RunPostBuildEvent>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <ProjectReference Include="..\Yuuna.Contracts\Yuuna.Contracts.csproj" />
-  </ItemGroup>
-
-  <Target Name="PostBuild" AfterTargets="PostBuildEvent">
-    <Exec Command="copy $(TargetPath) &quot;$(SolutionDir)src\Yuuna\bin\Debug\netcoreapp3.0\modules\M&quot;" />
-  </Target>
-
-</Project>

+ 31 - 17
src/Yuuna/EntryPoint.cs

@@ -16,26 +16,35 @@ namespace Yuuna
     using Yuuna.Common.Utilities;
     using System.Threading.Tasks;
     using System.Reflection;
+    using System.Linq;
 
     internal class EntryPoint
     {
         public static void Main(string[] args)
         {
-            var mc = new ModuleCollection();
-             
-            Console.WriteLine(mc._modules[mc.Names[0]].Name);
-            Console.ReadKey();
-
-            mc._modules[mc.Names[0]].Unload();
-            Console.WriteLine(mc._modules[mc.Names[0]].Name);
-            Console.ReadKey();
-
-            mc._modules[mc.Names[0]].Reload();
-            Console.WriteLine(mc._modules[mc.Names[0]].Name);
-            Console.ReadKey();
-            //Send("打開門");
+            //var mc = ModuleManager.Instance;
+            //foreach (var m in mc.Modules)
+            //{
+            //    Console.WriteLine(m.Name);
+            //}
+            //Console.WriteLine("----");
+            //Console.ReadKey();
+            //mc.UnloadAll();
+            //foreach (var m in mc.Modules)
+            //{
+            //    Console.WriteLine(m.Name);
+            //} 
+            //Console.WriteLine("----");
+            //Console.ReadKey();
+            //mc.ReloadAll();  
+            //foreach (var m in mc.Modules)
+            //{
+            //    Console.WriteLine(m.Name);
+            //}
+
+            Send("打開門");
             return;
-            
+
             Send("開燈");
             try
             {
@@ -74,7 +83,7 @@ namespace Yuuna
         public static void Send(string text)
         {
             var segmenter = new JiebaTextSegmenter() as ITextSegmenter;
-            var allPlugins = new ModuleBase[] { /*new Fake()*/ };
+            var allPlugins = ModuleManager.Instance.Modules;
             foreach (var item in allPlugins)
             {
                 item.Initialize(segmenter, new GroupManager());
@@ -86,7 +95,7 @@ namespace Yuuna
             Console.WriteLine($"來自分詞器 {segmenter.Name} 的分詞結果: [ {string.Join(", ", cutted)} ]");
 
             IStrategy plan = new DefaultStrategy();
-            var alternative = plan.FindBest(allPlugins, cutted);
+            var alternative = plan.FindBest(allPlugins.Select(x=>x.PatternSet).ToArray(), cutted);
             switch (alternative.Status)
             {
                 case Status.Invalid:
@@ -94,15 +103,20 @@ namespace Yuuna
                     break;
                 case Status.Optimal:
                     alternative.Matches[0].Pattern.Owner.Patterns.TryGet(alternative.Matches[0].Pattern, out var bag);
-                    bag.Invoke(alternative.Matches[0]);
+                    var r = bag.Invoke(alternative.Matches[0]);
+                    Console.WriteLine(r.Mood + " | " + r.Message);
                     break;
                 case Status.Closest:
+                    Console.WriteLine("closest");
                     break;
                 case Status.Proposition:
+                    Console.WriteLine("proposition");
                     break;
                 case Status.Paradox:
+                    Console.WriteLine("paradox");
                     break;
                 case Status.NoModuleInstalled:
+                    Console.WriteLine("noModuleInstalled");
                     break;
                 default:
                     break;

+ 0 - 41
src/Yuuna/ModuleCollection.cs

@@ -1,41 +0,0 @@
-// Author: Orlys
-// Github: https://github.com/Orlys
-
-namespace Yuuna
-{
-    using System;
-    using System.Collections.Generic;
-    using System.Linq;
-    using Yuuna.Contracts.Modules;
-    using System.Runtime.Loader;
-    using System.IO;
-    using System.Linq.Expressions;
-    using System.Reflection;
-     
-
-    public class ModuleCollection
-    {
-        public string[] Names => this._modules.Keys.ToArray();
-
-
-        public readonly Dictionary<string, ModuleDomain> _modules;
-         
-        public ModuleCollection()
-        {
-            this._modules = new Dictionary<string, ModuleDomain>();
-            var modulesFolder = new DirectoryInfo("./modules");
-            if (!modulesFolder.Exists)
-            {
-                modulesFolder.Create();
-            }
-            else
-            {
-                foreach (var moduleFolder in modulesFolder.EnumerateDirectories())
-                {
-                    if (ModuleDomain.TryCreate(moduleFolder, out var domain))
-                        this._modules.Add(domain.Name, domain);
-                }
-            }
-        }
-    }
-}

+ 85 - 0
src/Yuuna/Plugins/ModuleManager.cs

@@ -0,0 +1,85 @@
+// Author: Orlys
+// Github: https://github.com/Orlys
+
+namespace Yuuna
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Linq;
+    using Yuuna.Contracts.Modules;
+    using System.Runtime.Loader;
+    using System.IO;
+    using System.Linq.Expressions;
+    using System.Reflection;
+     
+
+    public class ModuleManager
+    {
+        private static volatile ModuleManager s_inst;
+        private static object s_lock = new object();
+        public static ModuleManager Instance
+        {
+            get
+            {
+                if(s_inst is null)
+                {
+                    lock (s_lock)
+                    {
+                        if(s_inst is null)
+                        {
+                            s_inst = new ModuleManager();
+                        }
+                    }
+                }
+                return s_inst;
+
+            }
+        }
+
+        private const string PATH = "./modules";
+
+        private readonly List< ModuleProxy> _modules;
+        private readonly DirectoryInfo _modulesFolder;
+
+        private ModuleManager()
+        {
+            this._modules = new List<ModuleProxy>();
+            this._modulesFolder = new DirectoryInfo(PATH);
+            if (!this._modulesFolder.Exists)
+            {
+                this._modulesFolder.Create();
+            }
+            else
+            {
+                this.Scan();
+            }
+        }
+
+        public IReadOnlyList<ModuleProxy> Modules => this._modules;
+
+        private void Scan()
+        {
+            foreach (var moduleFolder in this._modulesFolder.EnumerateDirectories())
+            {
+                if (ModuleProxy.TryCreate(moduleFolder, out var domain))
+                    this._modules.Add(domain);
+            }
+        }
+
+        public void UnloadAll()
+        {
+            foreach (var m in this._modules)
+            {
+                m.Unload();
+                (m as IDisposable)?.Dispose();
+            }
+            this._modules.Clear();
+        }
+
+        public void ReloadAll()
+        {
+            this.UnloadAll();
+            this.Scan();
+        }
+    }
+}

+ 24 - 9
src/Yuuna/ModuleDomain.cs → src/Yuuna/Plugins/ModuleProxy.cs

@@ -11,32 +11,39 @@ namespace Yuuna
     using System.Linq.Expressions;
     using System.IO;
     using System.Linq;
+    using System.Runtime.CompilerServices;
+    using Yuuna.Contracts.Patterns;
 
-    public sealed class ModuleDomain 
+    public sealed class ModuleProxy : IDisposable
     { 
         private volatile ModuleBase _inst;
         private volatile AssemblyLoadContext _loader; 
-        private readonly DirectoryInfo _moduleFolder;
+        private DirectoryInfo _moduleFolder;
+        private readonly object _lock = new object();
+        public bool Proxied => this._inst is null;
 
         public string Name => this._inst?.Name;
-        private ModuleDomain(DirectoryInfo moduleFolder, AssemblyLoadContext loader, ModuleBase inst)
+        private ModuleProxy(DirectoryInfo moduleFolder, AssemblyLoadContext loader, ModuleBase inst)
         {
             this._moduleFolder = moduleFolder;
             this._loader = loader;
             this._inst = inst; 
         }
 
-        public static bool TryCreate(DirectoryInfo moduleFolder, out ModuleDomain domain)
+        public IPatternSet PatternSet => this._inst.Patterns;
+
+        public static bool TryCreate(DirectoryInfo moduleFolder, out ModuleProxy domain)
         {
             if( LoadSingleModuleType(moduleFolder, out var loader, out var inst))
             {
-                domain = new ModuleDomain(moduleFolder, loader, inst);
+                domain = new ModuleProxy(moduleFolder, loader, inst);
                 return true;
             }
             domain = null;
             return false;
         }
-
+         
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private static bool LoadSingleModuleType(DirectoryInfo moduleFolder, out AssemblyLoadContext loader, out ModuleBase inst)
         {
             loader = new AssemblyLoadContext(moduleFolder.Name, true);
@@ -91,9 +98,12 @@ namespace Yuuna
         {
             if(LoadSingleModuleType(this._moduleFolder, out var loader, out var inst))
             {
-                this.Unload();
-                this._loader = loader;
-                this._inst = inst;
+                lock (this._lock)
+                {
+                    this.Unload();
+                    this._loader = loader;
+                    this._inst = inst;
+                }
             }
         }
 
@@ -105,5 +115,10 @@ namespace Yuuna
             this._loader?.Unload();
             this._loader = null; 
         }
+
+        void IDisposable.Dispose()
+        {
+            this.Unload();
+        }
     }
 }

+ 31 - 7
src/Yuuna/TextSegmention/JiebaSegmenter.cs

@@ -14,9 +14,16 @@ namespace Yuuna.TextSegmention
 
     public sealed class JiebaTextSegmenter : ITextSegmenter
     {
-        private readonly JiebaSegmenter _jieba = new JiebaSegmenter();
+        public JiebaTextSegmenter()
+        {
+            this._jieba = new JiebaSegmenter();
+            this.Culture = CultureInfo.GetCultureInfo("zh-TW"); 
+        }
+
+        private readonly JiebaSegmenter _jieba;
+
+        public CultureInfo Culture { get; } 
 
-        public CultureInfo Culture => CultureInfo.GetCultureInfo("zh-TW");
         string ITextSegmenter.Name => "Jieba.Net";
 
         public IImmutableList<string> Cut(string text) => this._jieba.Cut(text, true, true).ToImmutableArray();
@@ -31,19 +38,36 @@ namespace Yuuna.TextSegmention
                 this.InternalLog("name: " + name);
                 foreach (var synonym in manager[name].ToImmutable())
                 {
-                    this.InternalLog("  count: " + synonym.ToImmutable().Count);
                     foreach (var w in synonym.ToImmutable())
                     {
-                        this.InternalLog("  added: " + w);
                         this._jieba.AddWord(w);
+                        this.InternalLog("  added: " + w);
                     }
-                }
-                Console.WriteLine();
+                } 
             }
         }
 
-        private void InternalLog(string v)
+        public void Unload(IGroupManager manager)
         {
+            if (manager == null)
+                return;
+
+            foreach (var name in manager.Keys)
+            {
+                this.InternalLog("name: " + name);
+                foreach (var synonym in manager[name].ToImmutable())
+                {
+                    foreach (var w in synonym.ToImmutable())
+                    {
+                        this._jieba.DeleteWord(w);
+                        this.InternalLog("  deleted: " + w);
+                    }
+                } 
+            }
+        }
+
+        private void InternalLog(string v)
+        { 
             Console.WriteLine(v);
         }
     }

+ 0 - 52
src/Yuuna/TypeHelper.cs

@@ -1,52 +0,0 @@
-// Author: Orlys
-// Github: https://github.com/Orlys
-
-namespace Yuuna
-{
-    using System;
-    using System.Collections.Generic;
-    using System.Collections.Immutable;
-    using System.Reflection;
-
-    public static class TypeHelper
-    { 
-        public static bool ValidateAssemblyFile(string assemblyFile, out AssemblyName assemblyName)
-        {
-            try
-            {
-                assemblyName = AssemblyName.GetAssemblyName(assemblyFile);
-                return true;
-            }
-            catch
-            {
-            }
-            assemblyName = null;
-            return false;
-        }
-
-        public static IImmutableList<Type> ScanTypes(Assembly assembly)
-        {
-            var types = new List<Type>();
-            var set = new HashSet<Assembly>();
-            var stack = new Stack<Assembly>();
-            stack.Push(assembly);
-
-            while (stack.Count > 0)
-            {
-                var asm = stack.Pop();
-
-                if (asm.IsDynamic) continue;
-
-                types.AddRange(asm.DefinedTypes);
-
-                foreach (var other in asm.GetReferencedAssemblies())
-                {
-                    if (set.Add(asm))
-                        stack.Push(Assembly.Load(other));
-                }
-            }
-
-            return types.ToImmutableArray();
-        }
-    }
-}