parent
73dba8b1db
commit
d84b254716
|
@ -1,4 +1,5 @@
|
||||||
using Canon.Core.Enums;
|
using Canon.Core.Enums;
|
||||||
|
using Canon.Core.Exceptions;
|
||||||
using Canon.Core.GrammarParser;
|
using Canon.Core.GrammarParser;
|
||||||
using Canon.Core.LexicalParser;
|
using Canon.Core.LexicalParser;
|
||||||
using Canon.Core.SyntaxNodes;
|
using Canon.Core.SyntaxNodes;
|
||||||
|
@ -69,11 +70,11 @@ public interface IGrammarParser
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Run out of token but not accept");
|
throw new GrammarException(stack.Peek().State);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException("Failed to analyse input grammar");
|
throw new GrammarException(stack.Peek().State, enumerator.Current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,71 @@
|
||||||
|
using System.Text;
|
||||||
|
using Canon.Core.Abstractions;
|
||||||
|
using Canon.Core.GrammarParser;
|
||||||
|
using Canon.Core.LexicalParser;
|
||||||
|
|
||||||
namespace Canon.Core.Exceptions;
|
namespace Canon.Core.Exceptions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 语法分析中引发的异常
|
/// 语法分析中引发的异常
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class GrammarException : Exception
|
public class GrammarException : Exception
|
||||||
{
|
{
|
||||||
public GrammarException() { }
|
public override string Message { get; }
|
||||||
|
|
||||||
public GrammarException(string message) : base(message) { }
|
/// <summary>
|
||||||
|
/// 语法分析错误时的分析状态
|
||||||
|
/// </summary>
|
||||||
|
public ITransformer CurrentState { get; }
|
||||||
|
|
||||||
public GrammarException(string message, Exception innerException) : base(message, innerException) { }
|
/// <summary>
|
||||||
|
/// 语法分析错误时的输入符号
|
||||||
|
/// </summary>
|
||||||
|
public SemanticToken CurrentToken { get; }
|
||||||
|
|
||||||
|
public GrammarException(ITransformer currentState)
|
||||||
|
{
|
||||||
|
CurrentState = currentState;
|
||||||
|
CurrentToken = SemanticToken.End;
|
||||||
|
|
||||||
|
StringBuilder builder = new();
|
||||||
|
builder.Append("Except ");
|
||||||
|
|
||||||
|
foreach (TerminatorBase terminatorBase in ListNextTerminators(CurrentState))
|
||||||
|
{
|
||||||
|
builder.Append('\'').Append(terminatorBase).Append("' ");
|
||||||
|
}
|
||||||
|
|
||||||
|
Message = builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GrammarException(ITransformer currentState, SemanticToken currentToken)
|
||||||
|
{
|
||||||
|
CurrentState = currentState;
|
||||||
|
CurrentToken = currentToken;
|
||||||
|
|
||||||
|
StringBuilder builder = new();
|
||||||
|
builder.Append("Expect ");
|
||||||
|
|
||||||
|
foreach (TerminatorBase terminatorBase in ListNextTerminators(CurrentState))
|
||||||
|
{
|
||||||
|
builder.Append('\'').Append(terminatorBase).Append("',");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CurrentToken.Equals(SemanticToken.End))
|
||||||
|
{
|
||||||
|
builder.Append("but '").Append(CurrentToken.LiteralValue).Append("' found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Message = builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<TerminatorBase> ListNextTerminators(ITransformer state)
|
||||||
|
{
|
||||||
|
List<TerminatorBase> result = [];
|
||||||
|
|
||||||
|
result.AddRange(state.ShiftTable.Keys);
|
||||||
|
result.AddRange(state.ReduceTable.Keys);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
71
Canon.Tests/GrammarParserTests/PascalGrammarFailedTests.cs
Normal file
71
Canon.Tests/GrammarParserTests/PascalGrammarFailedTests.cs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
using Canon.Core.Exceptions;
|
||||||
|
using Canon.Tests.Utils;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace Canon.Tests.GrammarParserTests;
|
||||||
|
|
||||||
|
public class PascalGrammarFailedTests(ITestOutputHelper testOutputHelper)
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void StructTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
begin
|
||||||
|
end
|
||||||
|
""";
|
||||||
|
|
||||||
|
CatchException(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AssignTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
begin
|
||||||
|
a := 'a';
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
CatchException(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StatementTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
begin
|
||||||
|
if a = 1 then
|
||||||
|
doSomething;
|
||||||
|
else
|
||||||
|
doSomething;
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
CatchException(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ForTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
begin
|
||||||
|
for a = 1 to 100 do
|
||||||
|
doSomething
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
CatchException(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CatchException(string program)
|
||||||
|
{
|
||||||
|
GrammarException exception = Assert.Throws<GrammarException>(() =>
|
||||||
|
CompilerHelpers.Analyse(program));
|
||||||
|
|
||||||
|
testOutputHelper.WriteLine(exception.Message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,22 +50,6 @@ public class PascalGrammarTests
|
||||||
Assert.Equal("exFunction", root.Head.ProgramName.LiteralValue);
|
Assert.Equal("exFunction", root.Head.ProgramName.LiteralValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void SubprogramTest()
|
|
||||||
{
|
|
||||||
const string program = """
|
|
||||||
program main;
|
|
||||||
procedure test;
|
|
||||||
begin
|
|
||||||
end;
|
|
||||||
begin
|
|
||||||
end.
|
|
||||||
""";
|
|
||||||
|
|
||||||
ProgramStruct root = CompilerHelpers.Analyse(program);
|
|
||||||
Assert.Equal("main", root.Head.ProgramName.LiteralValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CharacterTest()
|
public void CharacterTest()
|
||||||
{
|
{
|
||||||
|
@ -94,4 +78,87 @@ public class PascalGrammarTests
|
||||||
|
|
||||||
CompilerHelpers.Analyse(program);
|
CompilerHelpers.Analyse(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MultiplyArrayTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program arrayTest;
|
||||||
|
var a : array [10..100, 0..10] of integer;
|
||||||
|
begin
|
||||||
|
a[10,0] := 1;
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
CompilerHelpers.Analyse(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ProcedureTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
procedure test;
|
||||||
|
begin
|
||||||
|
end;
|
||||||
|
begin
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
CompilerHelpers.Analyse(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FunctionTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
function test(a, b : integer) : integer;
|
||||||
|
begin
|
||||||
|
test := 1;
|
||||||
|
end;
|
||||||
|
begin
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
CompilerHelpers.Analyse(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ForLoopTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
var i : integer;
|
||||||
|
begin
|
||||||
|
for i := 1 to 100 do
|
||||||
|
begin
|
||||||
|
doSomething(i);
|
||||||
|
end;
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
CompilerHelpers.Analyse(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IfConditionTest()
|
||||||
|
{
|
||||||
|
const string program = """
|
||||||
|
program main;
|
||||||
|
begin
|
||||||
|
if 1 = 2 then
|
||||||
|
begin
|
||||||
|
test1;
|
||||||
|
test2 := a;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
doSomething;
|
||||||
|
end;
|
||||||
|
end.
|
||||||
|
""";
|
||||||
|
|
||||||
|
CompilerHelpers.Analyse(program);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user