add: Grammar builder test with a simple gramma.
This commit is contained in:
parent
6f8fb4c54e
commit
ff0b183f4f
|
@ -5,7 +5,7 @@ public class GrammarBuilder
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 指定文法的生成式
|
/// 指定文法的生成式
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<NonTerminator, List<List<TerminatorBase>>> Generators { get; } = [];
|
public Dictionary<NonTerminator, List<List<TerminatorBase>>> Generators { get; init; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 文法的起始符
|
/// 文法的起始符
|
||||||
|
@ -108,6 +108,7 @@ public class GrammarBuilder
|
||||||
{
|
{
|
||||||
// 指定表达式开头是终结符
|
// 指定表达式开头是终结符
|
||||||
Terminator terminator = (Terminator)expressionHead;
|
Terminator terminator = (Terminator)expressionHead;
|
||||||
|
|
||||||
result.Add(terminator);
|
result.Add(terminator);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -139,8 +140,18 @@ public class GrammarBuilder
|
||||||
{
|
{
|
||||||
changed = false;
|
changed = false;
|
||||||
|
|
||||||
|
// 不能在foreach过程中修改集合
|
||||||
|
// 因此需要在遍历完成之后添加
|
||||||
|
List<Expression> addedExpressions = [];
|
||||||
|
|
||||||
foreach (Expression e in closure)
|
foreach (Expression e in closure)
|
||||||
{
|
{
|
||||||
|
if (e.Pos >= e.Right.Count)
|
||||||
|
{
|
||||||
|
// 已经移进到达句型的末尾
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
TerminatorBase next = e.Right[e.Pos];
|
TerminatorBase next = e.Right[e.Pos];
|
||||||
|
|
||||||
if (next.IsTerminated)
|
if (next.IsTerminated)
|
||||||
|
@ -169,14 +180,22 @@ public class GrammarBuilder
|
||||||
Left = nonTerminator, Right = nextExpression, LookAhead = lookAhead
|
Left = nonTerminator, Right = nextExpression, LookAhead = lookAhead
|
||||||
};
|
};
|
||||||
|
|
||||||
if (closure.Add(newExpression))
|
if (!closure.Contains(newExpression))
|
||||||
|
{
|
||||||
|
addedExpressions.Add(newExpression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Expression addedExpression in addedExpressions)
|
||||||
|
{
|
||||||
|
if (closure.Add(addedExpression))
|
||||||
{
|
{
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return closure;
|
return closure;
|
||||||
}
|
}
|
||||||
|
@ -197,7 +216,8 @@ public class GrammarBuilder
|
||||||
bool added = true;
|
bool added = true;
|
||||||
while (added)
|
while (added)
|
||||||
{
|
{
|
||||||
added = false;
|
// 同样不能在foreach期间修改集合
|
||||||
|
HashSet<LrState> addedStates = [];
|
||||||
|
|
||||||
foreach (LrState state in Automation)
|
foreach (LrState state in Automation)
|
||||||
{
|
{
|
||||||
|
@ -206,20 +226,19 @@ public class GrammarBuilder
|
||||||
|
|
||||||
foreach (Expression e in state.Expressions)
|
foreach (Expression e in state.Expressions)
|
||||||
{
|
{
|
||||||
|
if (e.Pos >= e.Right.Count)
|
||||||
|
{
|
||||||
|
// 已经移进到达末尾
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminatorBase next = e.Right[e.Pos];
|
||||||
Expression nextExpression = new()
|
Expression nextExpression = new()
|
||||||
{
|
{
|
||||||
Left = e.Left, Right = e.Right, LookAhead = e.LookAhead, Pos = e.Pos
|
Left = e.Left, Right = e.Right, LookAhead = e.LookAhead, Pos = e.Pos
|
||||||
};
|
};
|
||||||
|
|
||||||
if (nextExpression.Pos >= nextExpression.Right.Count)
|
|
||||||
{
|
|
||||||
// 移进符号已经到达句型的末尾
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
nextExpression.Pos += 1;
|
nextExpression.Pos += 1;
|
||||||
|
|
||||||
TerminatorBase next = nextExpression.Right[nextExpression.Pos];
|
|
||||||
if (!nextExpressions.TryAdd(next, [nextExpression]))
|
if (!nextExpressions.TryAdd(next, [nextExpression]))
|
||||||
{
|
{
|
||||||
nextExpressions[next].Add(nextExpression);
|
nextExpressions[next].Add(nextExpression);
|
||||||
|
@ -241,18 +260,27 @@ public class GrammarBuilder
|
||||||
if (Automation.TryGetValue(newState, out LrState? oldState))
|
if (Automation.TryGetValue(newState, out LrState? oldState))
|
||||||
{
|
{
|
||||||
// 存在这个项目集闭包
|
// 存在这个项目集闭包
|
||||||
state.Transformer.Add(pair.Key, oldState);
|
state.AddTransform(pair.Key, oldState);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 不存在这个项目集闭包
|
// 不存在这个项目集闭包
|
||||||
Automation.Add(newState);
|
// 但是需要考虑该状态在addedStates集合中的情况
|
||||||
state.Transformer.Add(pair.Key, newState);
|
if (addedStates.TryGetValue(newState, out LrState? addedState))
|
||||||
|
{
|
||||||
|
state.AddTransform(pair.Key, addedState);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state.AddTransform(pair.Key, newState);
|
||||||
|
addedStates.Add(newState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
added = true;
|
added = addedStates.Count != 0;
|
||||||
}
|
Automation.UnionWith(addedStates);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Grammar { Begin = Begin, BeginState = beginState };
|
return new Grammar { Begin = Begin, BeginState = beginState };
|
||||||
|
|
|
@ -16,6 +16,28 @@ public class LrState : IEquatable<LrState>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<TerminatorBase, LrState> Transformer { get; } = [];
|
public Dictionary<TerminatorBase, LrState> Transformer { get; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 向状态中添加一个迁移规则
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="terminator">迁移的条件</param>
|
||||||
|
/// <param name="next">迁移到达的状态</param>
|
||||||
|
/// <exception cref="InvalidOperationException">如果在状态中已经存在该迁移规则且迁移到的状态和欲设置的状态不同
|
||||||
|
/// 抛出无效操作异常</exception>
|
||||||
|
public void AddTransform(TerminatorBase terminator, LrState next)
|
||||||
|
{
|
||||||
|
if (Transformer.TryGetValue(terminator, out LrState? state))
|
||||||
|
{
|
||||||
|
if (state != next)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("A terminator transform to two different states");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Transformer.Add(terminator, next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool Equals(LrState? other)
|
public bool Equals(LrState? other)
|
||||||
{
|
{
|
||||||
if (other is null)
|
if (other is null)
|
||||||
|
|
110
Canon.Tests/GrammarParserTests/SimpleGrammarTests.cs
Normal file
110
Canon.Tests/GrammarParserTests/SimpleGrammarTests.cs
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
using Canon.Core.Enums;
|
||||||
|
using Canon.Core.GrammarParser;
|
||||||
|
|
||||||
|
namespace Canon.Tests.GrammarParserTests;
|
||||||
|
|
||||||
|
public class SimpleGrammarTests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 用于测试的简单语法
|
||||||
|
/// S -> E
|
||||||
|
/// E -> E+T | E-T | T
|
||||||
|
/// T -> T*F | T/F | F
|
||||||
|
/// F -> (E) | n
|
||||||
|
/// 为了方便测试指定
|
||||||
|
/// E ProgramStruct
|
||||||
|
/// T ProgramBody
|
||||||
|
/// F StatementList
|
||||||
|
/// n Identifier
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Dictionary<NonTerminator, List<List<TerminatorBase>>> s_simpleGrammar = new()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
new NonTerminator(NonTerminatorType.StartNonTerminator), [
|
||||||
|
[new NonTerminator(NonTerminatorType.ProgramStruct)]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new NonTerminator(NonTerminatorType.ProgramStruct), [
|
||||||
|
[
|
||||||
|
new NonTerminator(NonTerminatorType.ProgramStruct), new Terminator(OperatorType.Plus),
|
||||||
|
new NonTerminator(NonTerminatorType.ProgramBody)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
new NonTerminator(NonTerminatorType.ProgramStruct), new Terminator(OperatorType.Minus),
|
||||||
|
new NonTerminator(NonTerminatorType.ProgramBody)
|
||||||
|
],
|
||||||
|
[new NonTerminator(NonTerminatorType.ProgramBody)]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new NonTerminator(NonTerminatorType.ProgramBody), [
|
||||||
|
[
|
||||||
|
new NonTerminator(NonTerminatorType.ProgramBody), new Terminator(OperatorType.Multiply),
|
||||||
|
new NonTerminator(NonTerminatorType.StatementList)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
new NonTerminator(NonTerminatorType.ProgramBody), new Terminator(OperatorType.Divide),
|
||||||
|
new NonTerminator(NonTerminatorType.StatementList)
|
||||||
|
],
|
||||||
|
[new NonTerminator(NonTerminatorType.StatementList)]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
new NonTerminator(NonTerminatorType.StatementList), [
|
||||||
|
[
|
||||||
|
new Terminator(DelimiterType.LeftParenthesis), new NonTerminator(NonTerminatorType.ProgramStruct),
|
||||||
|
new Terminator(DelimiterType.RightParenthesis)
|
||||||
|
],
|
||||||
|
[Terminator.IdentifierTerminator]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FirstSetTest()
|
||||||
|
{
|
||||||
|
GrammarBuilder builder = new()
|
||||||
|
{
|
||||||
|
Generators = s_simpleGrammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator)
|
||||||
|
};
|
||||||
|
|
||||||
|
builder.Build();
|
||||||
|
|
||||||
|
Assert.Contains(builder.FirstSet, pair =>
|
||||||
|
pair.Key == new NonTerminator(NonTerminatorType.StartNonTerminator));
|
||||||
|
Assert.Contains(builder.FirstSet, pair =>
|
||||||
|
pair.Key == new NonTerminator(NonTerminatorType.ProgramStruct));
|
||||||
|
Assert.Contains(builder.FirstSet, pair =>
|
||||||
|
pair.Key == new NonTerminator(NonTerminatorType.ProgramBody));
|
||||||
|
Assert.Contains(builder.FirstSet, pair =>
|
||||||
|
pair.Key == new NonTerminator(NonTerminatorType.StatementList));
|
||||||
|
|
||||||
|
foreach (HashSet<Terminator> terminators in builder.FirstSet.Values)
|
||||||
|
{
|
||||||
|
Assert.Contains(Terminator.IdentifierTerminator, terminators);
|
||||||
|
Assert.Contains(new Terminator(DelimiterType.LeftParenthesis), terminators);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StatsTest()
|
||||||
|
{
|
||||||
|
GrammarBuilder builder = new()
|
||||||
|
{
|
||||||
|
Generators = s_simpleGrammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator)
|
||||||
|
};
|
||||||
|
|
||||||
|
Grammar grammar = builder.Build();
|
||||||
|
|
||||||
|
Assert.Equal(30, builder.Automation.Count);
|
||||||
|
|
||||||
|
// 来自Ichirinko不辞辛劳的手算
|
||||||
|
Assert.Contains(new NonTerminator(NonTerminatorType.ProgramStruct), grammar.BeginState.Transformer.Keys);
|
||||||
|
Assert.Contains(new NonTerminator(NonTerminatorType.ProgramBody), grammar.BeginState.Transformer.Keys);
|
||||||
|
Assert.Contains(new NonTerminator(NonTerminatorType.StatementList),
|
||||||
|
grammar.BeginState.Transformer.Keys);
|
||||||
|
Assert.Contains(new Terminator(DelimiterType.LeftParenthesis), grammar.BeginState.Transformer.Keys);
|
||||||
|
Assert.Contains(Terminator.IdentifierTerminator, grammar.BeginState.Transformer.Keys);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user