parent
52ede9fe96
commit
8e9df3fc52
34
Katheryne.Tests/Mocks/MockParamModule.cs
Normal file
34
Katheryne.Tests/Mocks/MockParamModule.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
using Katheryne.Abstractions;
|
||||||
|
|
||||||
|
namespace Katheryne.Tests.Mocks;
|
||||||
|
|
||||||
|
public class MockParamModule : IParamsModule
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, Func<string>> _functions = new();
|
||||||
|
|
||||||
|
public string ModuleName => "test";
|
||||||
|
|
||||||
|
public MockParamModule()
|
||||||
|
{
|
||||||
|
_functions.Add("hello", Hello);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string this[string param]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Func<string> func = _functions[param];
|
||||||
|
return func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsParam(string param)
|
||||||
|
{
|
||||||
|
return _functions.ContainsKey(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string Hello()
|
||||||
|
{
|
||||||
|
return "Hello, Katheryne";
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,25 @@
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using Katheryne.Abstractions;
|
||||||
|
using Katheryne.Exceptions;
|
||||||
using Katheryne.Models;
|
using Katheryne.Models;
|
||||||
|
using Katheryne.Tests.Mocks;
|
||||||
|
|
||||||
namespace Katheryne.Tests;
|
namespace Katheryne.Tests;
|
||||||
|
|
||||||
public class StringFormatterTests
|
public class StringFormatterTests
|
||||||
{
|
{
|
||||||
|
private readonly Dictionary<string, IParamsModule> _modules = new();
|
||||||
|
|
||||||
|
public StringFormatterTests()
|
||||||
|
{
|
||||||
|
var module = new MockParamModule();
|
||||||
|
_modules.Add(module.ModuleName, module);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FormatTest1()
|
public void FormatTest1()
|
||||||
{
|
{
|
||||||
StringFormatter formatter = new("Hello $1");
|
StringFormatter formatter = new("Hello $1", _modules);
|
||||||
|
|
||||||
Regex regex = new("I'm (.*)");
|
Regex regex = new("I'm (.*)");
|
||||||
|
|
||||||
|
@ -23,7 +34,7 @@ public class StringFormatterTests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void FormatTest2()
|
public void FormatTest2()
|
||||||
{
|
{
|
||||||
StringFormatter formatter = new("$你好呀 $1, 欢迎你离开垃圾的$2.");
|
StringFormatter formatter = new("$你好呀 $1, 欢迎你离开垃圾的$2.", _modules);
|
||||||
|
|
||||||
Regex regex = new("你好,我是(.*),我来自(.*)\\.");
|
Regex regex = new("你好,我是(.*),我来自(.*)\\.");
|
||||||
|
|
||||||
|
@ -34,4 +45,21 @@ public class StringFormatterTests
|
||||||
|
|
||||||
Assert.Equal("$你好呀 Ichirinko, 欢迎你离开垃圾的Trinity.", formatter.Format(match.Groups));
|
Assert.Equal("$你好呀 Ichirinko, 欢迎你离开垃圾的Trinity.", formatter.Format(match.Groups));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParamFormatTest1()
|
||||||
|
{
|
||||||
|
StringFormatter formatter = new("Test @test/hello", _modules);
|
||||||
|
|
||||||
|
Regex regex = new(".*?");
|
||||||
|
Match match = regex.Match("Test Input");
|
||||||
|
|
||||||
|
Assert.Equal("Test Hello, Katheryne", formatter.Format(match.Groups));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ParamFormatTest2()
|
||||||
|
{
|
||||||
|
Assert.Throws<GrammarException>(() => new StringFormatter("Test @test/nonexistent", _modules));
|
||||||
|
}
|
||||||
}
|
}
|
13
Katheryne/Abstractions/IParamsModule.cs
Normal file
13
Katheryne/Abstractions/IParamsModule.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
namespace Katheryne.Abstractions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 参数实现模块
|
||||||
|
/// </summary>
|
||||||
|
public interface IParamsModule
|
||||||
|
{
|
||||||
|
public string ModuleName { get; }
|
||||||
|
|
||||||
|
public string this[string param] { get; }
|
||||||
|
|
||||||
|
public bool ContainsParam(string param);
|
||||||
|
}
|
|
@ -59,14 +59,16 @@ public class KatheryneChatRobot : IChatRobot
|
||||||
if (answer.IsFormat)
|
if (answer.IsFormat)
|
||||||
{
|
{
|
||||||
string temp = answer.Format(match.Groups);
|
string temp = answer.Format(match.Groups);
|
||||||
_logger.LogInformation("Format answer {} to {}.", answer.RowString, temp);
|
_logger.LogInformation("Format answer {} to {}.",
|
||||||
|
answer.RowString, temp);
|
||||||
result.Add(temp);
|
result.Add(temp);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result.Add(answer.RowString);
|
result.Add(answer.RowString);
|
||||||
}
|
}
|
||||||
_logger.LogInformation("Moving to stage {} on input {}.", _currentStage, input);
|
_logger.LogInformation("Moving to stage {} on input {}.",
|
||||||
|
_currentStage, input);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +95,8 @@ public class KatheryneChatRobot : IChatRobot
|
||||||
_currentStage = transformer.NextStage;
|
_currentStage = transformer.NextStage;
|
||||||
result.Add(_grammarTree[_currentStage].Answer.RowString);
|
result.Add(_grammarTree[_currentStage].Answer.RowString);
|
||||||
|
|
||||||
_logger.LogInformation("Moving to stage {} with empty transform.", _currentStage);
|
_logger.LogInformation("Moving to stage {} with empty transform.",
|
||||||
|
_currentStage);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3
Katheryne/Models/GrammarParam.cs
Normal file
3
Katheryne/Models/GrammarParam.cs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
namespace Katheryne.Models;
|
||||||
|
|
||||||
|
public record GrammarParam(string OriginString, string Module, string Param);
|
|
@ -1,3 +1,4 @@
|
||||||
|
using Katheryne.Abstractions;
|
||||||
using Katheryne.Exceptions;
|
using Katheryne.Exceptions;
|
||||||
|
|
||||||
namespace Katheryne.Models;
|
namespace Katheryne.Models;
|
||||||
|
@ -6,11 +7,11 @@ public class GrammarTree
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, InnerStage> _stages = new();
|
private readonly Dictionary<string, InnerStage> _stages = new();
|
||||||
|
|
||||||
public GrammarTree(LexicalModel model)
|
public GrammarTree(LexicalModel model, Dictionary<string, IParamsModule> modules)
|
||||||
{
|
{
|
||||||
foreach (Stage stage in model.Stages)
|
foreach (Stage stage in model.Stages)
|
||||||
{
|
{
|
||||||
_stages[stage.Name] = new InnerStage(stage);
|
_stages[stage.Name] = new InnerStage(stage, modules);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Validate(model))
|
if (!Validate(model))
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
using Katheryne.Abstractions;
|
||||||
|
|
||||||
namespace Katheryne.Models;
|
namespace Katheryne.Models;
|
||||||
|
|
||||||
internal class InnerStage
|
internal class InnerStage
|
||||||
|
@ -8,10 +10,10 @@ internal class InnerStage
|
||||||
|
|
||||||
public StringFormatter Answer { get; }
|
public StringFormatter Answer { get; }
|
||||||
|
|
||||||
public InnerStage(Stage stage)
|
public InnerStage(Stage stage, Dictionary<string, IParamsModule> modules)
|
||||||
{
|
{
|
||||||
Name = stage.Name;
|
Name = stage.Name;
|
||||||
Answer = new StringFormatter(stage.Answer);
|
Answer = new StringFormatter(stage.Answer, modules);
|
||||||
|
|
||||||
foreach (Transformer transformer in stage.Transformers)
|
foreach (Transformer transformer in stage.Transformers)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using Katheryne.Abstractions;
|
||||||
|
using Katheryne.Exceptions;
|
||||||
|
|
||||||
namespace Katheryne.Models;
|
namespace Katheryne.Models;
|
||||||
|
|
||||||
|
@ -7,12 +9,20 @@ namespace Katheryne.Models;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class StringFormatter
|
public class StringFormatter
|
||||||
{
|
{
|
||||||
private readonly string _originString;
|
private static readonly HashSet<char> s_delimiters = new()
|
||||||
private readonly List<FormatTag> _formatTags = new();
|
{
|
||||||
|
',', '.', '!', ';', '?', ' ', ',', '。', ';'
|
||||||
|
};
|
||||||
|
|
||||||
public StringFormatter(string originString)
|
private readonly string _originString;
|
||||||
|
private readonly Dictionary<string, IParamsModule> _modules;
|
||||||
|
private readonly List<FormatTag> _formatTags = new();
|
||||||
|
private readonly List<GrammarParam> _params = new();
|
||||||
|
|
||||||
|
public StringFormatter(string originString, Dictionary<string, IParamsModule> modules)
|
||||||
{
|
{
|
||||||
_originString = originString;
|
_originString = originString;
|
||||||
|
_modules = modules;
|
||||||
|
|
||||||
GetFormatTags();
|
GetFormatTags();
|
||||||
}
|
}
|
||||||
|
@ -35,22 +45,33 @@ public class StringFormatter
|
||||||
result = result.Replace(tag.Value, collection[tag.Index].Value);
|
result = result.Replace(tag.Value, collection[tag.Index].Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (GrammarParam param in _params)
|
||||||
|
{
|
||||||
|
result = result.Replace(param.OriginString,
|
||||||
|
_modules[param.Module][param.Param]);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetFormatTags()
|
private void GetFormatTags()
|
||||||
{
|
{
|
||||||
List<int> indexes = new();
|
List<int> tagIndices = new();
|
||||||
|
List<int> paramIndices = new();
|
||||||
|
|
||||||
for (var i = 0; i < _originString.Length; i++)
|
for (var i = 0; i < _originString.Length; i++)
|
||||||
{
|
{
|
||||||
if (_originString[i] == '$')
|
if (_originString[i] == '$')
|
||||||
{
|
{
|
||||||
indexes.Add(i);
|
tagIndices.Add(i);
|
||||||
|
}
|
||||||
|
else if (_originString[i] == '@')
|
||||||
|
{
|
||||||
|
paramIndices.Add(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (int index in indexes)
|
foreach (int index in tagIndices)
|
||||||
{
|
{
|
||||||
string value = string.Empty;
|
string value = string.Empty;
|
||||||
int pos = index + 1;
|
int pos = index + 1;
|
||||||
|
@ -74,5 +95,43 @@ public class StringFormatter
|
||||||
|
|
||||||
_formatTags.Add(new FormatTag('$' + value, int.Parse(value)));
|
_formatTags.Add(new FormatTag('$' + value, int.Parse(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<char> chars = new();
|
||||||
|
foreach (int index in paramIndices)
|
||||||
|
{
|
||||||
|
chars.Clear();
|
||||||
|
int i = index + 1;
|
||||||
|
|
||||||
|
while (i < _originString.Length && !s_delimiters.Contains(_originString[i]))
|
||||||
|
{
|
||||||
|
chars.Add(_originString[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
string text = new(chars.ToArray());
|
||||||
|
|
||||||
|
string[] array = text.Split('/');
|
||||||
|
|
||||||
|
if (array.Length != 2)
|
||||||
|
{
|
||||||
|
throw new GrammarException($"Failed to parse grammar param: {text}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_modules.ContainsKey(array[0]))
|
||||||
|
{
|
||||||
|
throw new GrammarException($"Unknown module {array[0]}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_modules[array[0]].ContainsParam(array[1]))
|
||||||
|
{
|
||||||
|
throw new GrammarException($"Module {array[0]} doesn't support {array[1]}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_params.Add(new GrammarParam(
|
||||||
|
'@' + text,
|
||||||
|
array[0],
|
||||||
|
array[1]
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,6 +13,8 @@ public class KatheryneChatRobotFactory : IChatRobotFactory
|
||||||
private readonly ILogger<KatheryneChatRobot> _robotLogger;
|
private readonly ILogger<KatheryneChatRobot> _robotLogger;
|
||||||
private readonly DefaultChatRobot _defaultChatRobot;
|
private readonly DefaultChatRobot _defaultChatRobot;
|
||||||
|
|
||||||
|
private readonly Dictionary<string, IParamsModule> _modules = new();
|
||||||
|
|
||||||
private Grammar? _grammar;
|
private Grammar? _grammar;
|
||||||
|
|
||||||
public string GrammarText { get; private set; } = string.Empty;
|
public string GrammarText { get; private set; } = string.Empty;
|
||||||
|
@ -48,7 +50,8 @@ public class KatheryneChatRobotFactory : IChatRobotFactory
|
||||||
{
|
{
|
||||||
throw new GrammarException("Failed to parse lexical model.", ex);
|
throw new GrammarException("Failed to parse lexical model.", ex);
|
||||||
}
|
}
|
||||||
_grammar = new Grammar(new GrammarTree(model), model.RobotName, model.BeginStageName);
|
_grammar = new Grammar(new GrammarTree(model, _modules),
|
||||||
|
model.RobotName, model.BeginStageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IChatRobot GetRobot()
|
public IChatRobot GetRobot()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user