Further improved grammar parser test coverage (up to 92%)
This commit is contained in:
parent
bff41eef37
commit
9f694c584c
|
@ -12,16 +12,48 @@ namespace LLama.Unittest
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class GrammarParserTest
|
public sealed class GrammarParserTest
|
||||||
{
|
{
|
||||||
|
private static void CheckGrammar(string grammar, string rootRule, List<KeyValuePair<string, uint>> expected, List<LLamaGrammarElement> expectedRules)
|
||||||
|
{
|
||||||
|
var state = Grammar.Parse(grammar, rootRule);
|
||||||
|
Assert.Equal(0ul, state.StartRuleIndex);
|
||||||
|
|
||||||
|
foreach (var symbol in expected)
|
||||||
|
{
|
||||||
|
var rule = state.Rules[(int)symbol.Value];
|
||||||
|
Assert.Equal(symbol.Key, rule.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint index = 0;
|
||||||
|
foreach (var rule in state.Rules)
|
||||||
|
{
|
||||||
|
// compare rule to expected rule
|
||||||
|
for (uint i = 0; i < rule.Elements.Count; i++)
|
||||||
|
{
|
||||||
|
var element = rule.Elements[(int)i];
|
||||||
|
var expectedElement = expectedRules[(int)index];
|
||||||
|
|
||||||
|
// Pretty print error message before asserting
|
||||||
|
if (expectedElement.Type != element.Type || expectedElement.Value != element.Value)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"index: {index}");
|
||||||
|
Console.Error.WriteLine($"expected_element: {expectedElement.Type}, {expectedElement.Value}");
|
||||||
|
Console.Error.WriteLine($"actual_element: {element.Type}, {element.Value}");
|
||||||
|
Console.Error.WriteLine("expected_element != actual_element");
|
||||||
|
}
|
||||||
|
Assert.Equal(expectedElement.Type, element.Type);
|
||||||
|
Assert.Equal(expectedElement.Value, element.Value);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert.NotEmpty(state.Rules);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ParseComplexGrammar()
|
public void ParseComplexGrammar()
|
||||||
{
|
{
|
||||||
GBNFGrammarParser parsedGrammar = new GBNFGrammarParser();
|
var grammarBytes = @"root ::= (expr ""="" term ""\n"")+
|
||||||
string grammarBytes = @"root ::= (expr ""="" term ""\n"")+
|
expr ::= term ([-\x2b\x2A/] term)*
|
||||||
expr ::= term ([-+*/] term)*
|
term ::= [\x30-\x39]+";
|
||||||
term ::= [0-9]+";
|
|
||||||
|
|
||||||
var state = parsedGrammar.Parse(grammarBytes, "root");
|
|
||||||
Assert.Equal(0ul, state.StartRuleIndex);
|
|
||||||
|
|
||||||
var expected = new List<KeyValuePair<string, uint>>
|
var expected = new List<KeyValuePair<string, uint>>
|
||||||
{
|
{
|
||||||
|
@ -35,12 +67,6 @@ namespace LLama.Unittest
|
||||||
new KeyValuePair<string, uint>("term_7", 7),
|
new KeyValuePair<string, uint>("term_7", 7),
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var symbol in expected)
|
|
||||||
{
|
|
||||||
var rule = state.Rules[(int)symbol.Value];
|
|
||||||
Assert.Equal(symbol.Key, rule.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedRules = new List<LLamaGrammarElement>
|
var expectedRules = new List<LLamaGrammarElement>
|
||||||
{
|
{
|
||||||
new LLamaGrammarElement(LLamaGrammarElementType.RULE_REF, 4),
|
new LLamaGrammarElement(LLamaGrammarElementType.RULE_REF, 4),
|
||||||
|
@ -79,35 +105,12 @@ namespace LLama.Unittest
|
||||||
new LLamaGrammarElement(LLamaGrammarElementType.END, 0),
|
new LLamaGrammarElement(LLamaGrammarElementType.END, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
uint index = 0;
|
CheckGrammar(grammarBytes, "root", expected, expectedRules);
|
||||||
foreach (var rule in state.Rules)
|
|
||||||
{
|
|
||||||
// compare rule to expected rule
|
|
||||||
for (uint i = 0; i < rule.Elements.Count; i++)
|
|
||||||
{
|
|
||||||
var element = rule.Elements[(int)i];
|
|
||||||
var expectedElement = expectedRules[(int)index];
|
|
||||||
|
|
||||||
// Pretty print error message before asserting
|
|
||||||
if (expectedElement.Type != element.Type || expectedElement.Value != element.Value)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine($"index: {index}");
|
|
||||||
Console.Error.WriteLine($"expected_element: {expectedElement.Type}, {expectedElement.Value}");
|
|
||||||
Console.Error.WriteLine($"actual_element: {element.Type}, {element.Value}");
|
|
||||||
Console.Error.WriteLine("expected_element != actual_element");
|
|
||||||
}
|
|
||||||
Assert.Equal(expectedElement.Type, element.Type);
|
|
||||||
Assert.Equal(expectedElement.Value, element.Value);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Assert.NotEmpty(state.Rules);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ParseExtraComplexGrammar()
|
public void ParseExtraComplexGrammar()
|
||||||
{
|
{
|
||||||
GBNFGrammarParser parsedGrammar = new GBNFGrammarParser();
|
|
||||||
string grammarBytes = @"
|
string grammarBytes = @"
|
||||||
root ::= (expr ""="" ws term ""\n"")+
|
root ::= (expr ""="" ws term ""\n"")+
|
||||||
expr ::= term ([-+*/] term)*
|
expr ::= term ([-+*/] term)*
|
||||||
|
@ -117,9 +120,6 @@ namespace LLama.Unittest
|
||||||
ws ::= [ \t\n]*
|
ws ::= [ \t\n]*
|
||||||
";
|
";
|
||||||
|
|
||||||
var state = parsedGrammar.Parse(grammarBytes, "root");
|
|
||||||
Assert.Equal(0ul, state.StartRuleIndex);
|
|
||||||
|
|
||||||
var expected = new List<KeyValuePair<string, uint>>
|
var expected = new List<KeyValuePair<string, uint>>
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, uint>("expr", 2),
|
new KeyValuePair<string, uint>("expr", 2),
|
||||||
|
@ -137,12 +137,6 @@ namespace LLama.Unittest
|
||||||
new KeyValuePair<string, uint>("ws_12", 12),
|
new KeyValuePair<string, uint>("ws_12", 12),
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var symbol in expected)
|
|
||||||
{
|
|
||||||
var rule = state.Rules[(int)symbol.Value];
|
|
||||||
Assert.Equal(symbol.Key, rule.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
var expectedRules = new List<LLamaGrammarElement>
|
var expectedRules = new List<LLamaGrammarElement>
|
||||||
{
|
{
|
||||||
new LLamaGrammarElement(LLamaGrammarElementType.RULE_REF, 5),
|
new LLamaGrammarElement(LLamaGrammarElementType.RULE_REF, 5),
|
||||||
|
@ -214,31 +208,68 @@ namespace LLama.Unittest
|
||||||
new LLamaGrammarElement(LLamaGrammarElementType.END, 0)
|
new LLamaGrammarElement(LLamaGrammarElementType.END, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
uint index = 0;
|
CheckGrammar(grammarBytes, "root", expected, expectedRules);
|
||||||
foreach (var rule in state.Rules)
|
|
||||||
{
|
|
||||||
// compare rule to expected rule
|
|
||||||
for (uint i = 0; i < rule.Elements.Count; i++)
|
|
||||||
{
|
|
||||||
var element = rule.Elements[(int)i];
|
|
||||||
var expectedElement = expectedRules[(int)index];
|
|
||||||
|
|
||||||
// Pretty print error message before asserting
|
|
||||||
if (expectedElement.Type != element.Type || expectedElement.Value != element.Value)
|
|
||||||
{
|
|
||||||
Console.Error.WriteLine($"index: {index}");
|
|
||||||
Console.Error.WriteLine($"expected_element: {expectedElement.Type}, {expectedElement.Value}");
|
|
||||||
Console.Error.WriteLine($"actual_element: {element.Type}, {element.Value}");
|
|
||||||
Console.Error.WriteLine("expected_element != actual_element");
|
|
||||||
}
|
|
||||||
Assert.Equal(expectedElement.Type, element.Type);
|
|
||||||
Assert.Equal(expectedElement.Value, element.Value);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Assert.NotEmpty(state.Rules);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InvalidGrammarNoClosingBracket()
|
||||||
|
{
|
||||||
|
var parsedGrammar = new GBNFGrammarParser();
|
||||||
|
var grammarBytes = @"
|
||||||
|
root ::= (expr ""="" ws term ""\n""+ ## <--- Mismatched brackets on this line
|
||||||
|
expr ::= term ([-+*/] term)*
|
||||||
|
term ::= ident | num | ""("" ws expr "")"" ws
|
||||||
|
ident ::= [a-z] [a-z0-9_]* ws
|
||||||
|
num ::= [0-9]+ ws
|
||||||
|
ws ::= [ \t\n]*
|
||||||
|
";
|
||||||
|
|
||||||
|
Assert.Throws<GrammarExpectedNext>(() =>
|
||||||
|
{
|
||||||
|
parsedGrammar.Parse(grammarBytes, "root");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InvalidGrammarNoName()
|
||||||
|
{
|
||||||
|
var parsedGrammar = new GBNFGrammarParser();
|
||||||
|
var grammarBytes = @"
|
||||||
|
root ::= (expr ""="" ws term ""\n"")+
|
||||||
|
::= term ([-+*/] term)* ## <--- Missing a name for this rule!
|
||||||
|
term ::= ident | num | ""("" ws expr "")"" ws
|
||||||
|
ident ::= [a-z] [a-z0-9_]* ws
|
||||||
|
num ::= [0-9]+ ws
|
||||||
|
ws ::= [ \t\n]*
|
||||||
|
";
|
||||||
|
|
||||||
|
Assert.Throws<GrammarExpectedName>(() =>
|
||||||
|
{
|
||||||
|
parsedGrammar.Parse(grammarBytes, "root");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void InvalidGrammarBadHex()
|
||||||
|
{
|
||||||
|
var parsedGrammar = new GBNFGrammarParser();
|
||||||
|
var grammarBytes = @"
|
||||||
|
root ::= (expr ""="" ws term ""\n"")+
|
||||||
|
expr ::= term ([-+*/] term)*
|
||||||
|
term ::= ident | num | ""("" ws expr "")"" ws
|
||||||
|
ident ::= [a-z] [a-z0-9_]* ws
|
||||||
|
num ::= [0-\xQQ]+ ws ## <--- `\xQQ` is not valid hex!
|
||||||
|
ws ::= [ \t\n]*
|
||||||
|
";
|
||||||
|
|
||||||
|
Assert.Throws<GrammarUnexpectedHexCharsCount>(() =>
|
||||||
|
{
|
||||||
|
parsedGrammar.Parse(grammarBytes, "root");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void InvalidRuleNoElements()
|
public void InvalidRuleNoElements()
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue