@@ -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);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user