11
									
								
								Canon.Core/Abstractions/ICCodeGenerator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Canon.Core/Abstractions/ICCodeGenerator.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
using Canon.Core.CodeGenerators;
 | 
			
		||||
 | 
			
		||||
namespace Canon.Core.Abstractions;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 支持生成C语言代码的接口
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface ICCodeGenerator
 | 
			
		||||
{
 | 
			
		||||
    public void GenerateCCode(CCodeBuilder builder);
 | 
			
		||||
}
 | 
			
		||||
@@ -8,13 +8,19 @@ namespace Canon.Core.Abstractions;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 语法分析器接口
 | 
			
		||||
/// </summary>
 | 
			
		||||
public abstract class GrammarParserBase
 | 
			
		||||
public interface IGrammarParser
 | 
			
		||||
{
 | 
			
		||||
    public abstract ITransformer BeginTransformer { get; }
 | 
			
		||||
    public ITransformer BeginTransformer { get; }
 | 
			
		||||
 | 
			
		||||
    public abstract NonTerminator Begin { get; }
 | 
			
		||||
    public NonTerminator Begin { get; }
 | 
			
		||||
 | 
			
		||||
    public SyntaxNodeBase Analyse(IEnumerable<SemanticToken> tokens)
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 分析指定的词法记号流并构建对应的语法树
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="tokens">输入的词法记号流</param>
 | 
			
		||||
    /// <returns>语法树的根节点</returns>
 | 
			
		||||
    /// <exception cref="InvalidOperationException">语法分析错误</exception>
 | 
			
		||||
    public ProgramStruct Analyse(IEnumerable<SemanticToken> tokens)
 | 
			
		||||
    {
 | 
			
		||||
        Stack<AnalyseState> stack = [];
 | 
			
		||||
        stack.Push(new AnalyseState(BeginTransformer, SyntaxNodeBase.Create(SemanticToken.End)));
 | 
			
		||||
@@ -36,7 +42,7 @@ public abstract class GrammarParserBase
 | 
			
		||||
                {
 | 
			
		||||
                    // 如果是归约到起始符
 | 
			
		||||
                    // 那么就直接返回不继续进行归约
 | 
			
		||||
                    return top.Node;
 | 
			
		||||
                    return top.Node.Convert<ProgramStruct>();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                List<SyntaxNodeBase> children = [];
 | 
			
		||||
							
								
								
									
										11
									
								
								Canon.Core/Abstractions/ILexer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Canon.Core/Abstractions/ILexer.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
using Canon.Core.LexicalParser;
 | 
			
		||||
 | 
			
		||||
namespace Canon.Core.Abstractions;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 词法分析器接口
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface ILexer
 | 
			
		||||
{
 | 
			
		||||
    public IEnumerable<SemanticToken> Tokenize(ISourceReader reader);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								Canon.Core/Abstractions/ISourceReader.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								Canon.Core/Abstractions/ISourceReader.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
using System.Diagnostics.CodeAnalysis;
 | 
			
		||||
 | 
			
		||||
namespace Canon.Core.Abstractions;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 读取源代码的接口
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface ISourceReader
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 尝试读取下一个字符
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="c">读取到的字符</param>
 | 
			
		||||
    /// <returns>是否成功读取</returns>
 | 
			
		||||
    public bool TryReadChar([NotNullWhen(true)] out char? c);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 源文件名称
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string FileName { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 当前读取字符的行号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public uint Line { get; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 当前读取字符的列号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public uint Pos { get; }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								Canon.Core/CodeGenerators/CCodeBuilder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Canon.Core/CodeGenerators/CCodeBuilder.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Canon.Core.CodeGenerators;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 构建C语言代码
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class CCodeBuilder
 | 
			
		||||
{
 | 
			
		||||
    private readonly StringBuilder _builder = new();
 | 
			
		||||
 | 
			
		||||
    public void AddString(string code)
 | 
			
		||||
    {
 | 
			
		||||
        _builder.Append(code);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public string Build()
 | 
			
		||||
    {
 | 
			
		||||
        return _builder.ToString();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -22,7 +22,7 @@ public class Grammar
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public required LrState BeginState { get; init; }
 | 
			
		||||
 | 
			
		||||
    public GrammarParserBase ToGrammarParser()
 | 
			
		||||
    public IGrammarParser ToGrammarParser()
 | 
			
		||||
    {
 | 
			
		||||
        Dictionary<LrState, Transformer> transformers = [];
 | 
			
		||||
 | 
			
		||||
@@ -71,10 +71,10 @@ public class Grammar
 | 
			
		||||
        return new GrammarParser(transformers[BeginState], Begin);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class GrammarParser(ITransformer beginTransformer, NonTerminator begin) : GrammarParserBase
 | 
			
		||||
    private class GrammarParser(ITransformer beginTransformer, NonTerminator begin) : IGrammarParser
 | 
			
		||||
    {
 | 
			
		||||
        public override ITransformer BeginTransformer { get; } = beginTransformer;
 | 
			
		||||
        public override NonTerminator Begin { get; } = begin;
 | 
			
		||||
        public ITransformer BeginTransformer { get; } = beginTransformer;
 | 
			
		||||
        public NonTerminator Begin { get; } = begin;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class Transformer : ITransformer
 | 
			
		||||
 
 | 
			
		||||
@@ -21,12 +21,14 @@ public abstract class NonTerminatedSyntaxNode : SyntaxNodeBase, IEnumerable<Synt
 | 
			
		||||
            {
 | 
			
		||||
                yield return child;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            NonTerminatedSyntaxNode nonTerminatedNode = child.Convert<NonTerminatedSyntaxNode>();
 | 
			
		||||
 | 
			
		||||
            foreach (SyntaxNodeBase node in nonTerminatedNode)
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                yield return node;
 | 
			
		||||
                NonTerminatedSyntaxNode nonTerminatedNode = child.Convert<NonTerminatedSyntaxNode>();
 | 
			
		||||
 | 
			
		||||
                foreach (SyntaxNodeBase node in nonTerminatedNode)
 | 
			
		||||
                {
 | 
			
		||||
                    yield return node;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
using Canon.Core.Enums;
 | 
			
		||||
using Canon.Core.CodeGenerators;
 | 
			
		||||
using Canon.Core.Enums;
 | 
			
		||||
 | 
			
		||||
namespace Canon.Core.SyntaxNodes;
 | 
			
		||||
 | 
			
		||||
@@ -20,4 +21,9 @@ public class ProgramStruct : NonTerminatedSyntaxNode
 | 
			
		||||
    {
 | 
			
		||||
        return new ProgramStruct { Children = children };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public override void GenerateCCode(CCodeBuilder builder)
 | 
			
		||||
    {
 | 
			
		||||
        builder.AddString("#include <PascalCoreLib.h>");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
using Canon.Core.Enums;
 | 
			
		||||
using Canon.Core.Abstractions;
 | 
			
		||||
using Canon.Core.CodeGenerators;
 | 
			
		||||
using Canon.Core.Enums;
 | 
			
		||||
using Canon.Core.LexicalParser;
 | 
			
		||||
 | 
			
		||||
namespace Canon.Core.SyntaxNodes;
 | 
			
		||||
 | 
			
		||||
public abstract class SyntaxNodeBase
 | 
			
		||||
public abstract class SyntaxNodeBase : ICCodeGenerator
 | 
			
		||||
{
 | 
			
		||||
    public abstract bool IsTerminated { get; }
 | 
			
		||||
 | 
			
		||||
@@ -19,6 +21,14 @@ public abstract class SyntaxNodeBase
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 语法树节点基类对于生成C代码的默认实现
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public virtual void GenerateCCode(CCodeBuilder builder)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static SyntaxNodeBase Create(SemanticToken token)
 | 
			
		||||
    {
 | 
			
		||||
        return new TerminatedSyntaxNode { Token = token };
 | 
			
		||||
 
 | 
			
		||||
@@ -7,11 +7,11 @@ namespace Canon.Generator.GrammarGenerator;
 | 
			
		||||
public class GeneratedGrammarParser(
 | 
			
		||||
    Dictionary<string, GeneratedTransformer> transformers,
 | 
			
		||||
    string beginState,
 | 
			
		||||
    NonTerminator begin) : GrammarParserBase
 | 
			
		||||
    NonTerminator begin) : IGrammarParser
 | 
			
		||||
{
 | 
			
		||||
    public override ITransformer BeginTransformer => transformers[beginState];
 | 
			
		||||
    public ITransformer BeginTransformer => transformers[beginState];
 | 
			
		||||
 | 
			
		||||
    public override NonTerminator Begin => begin;
 | 
			
		||||
    public NonTerminator Begin => begin;
 | 
			
		||||
 | 
			
		||||
    public string GenerateCode(string namespaceValue)
 | 
			
		||||
    {
 | 
			
		||||
@@ -74,7 +74,7 @@ public class GeneratedGrammarParser(
 | 
			
		||||
                       """);
 | 
			
		||||
        builder.Append('\n');
 | 
			
		||||
 | 
			
		||||
        builder.Append("public class GeneratedGrammarParser : GrammarParserBase\n")
 | 
			
		||||
        builder.Append("public class GeneratedGrammarParser : IGrammarParser\n")
 | 
			
		||||
            .Append("{\n");
 | 
			
		||||
 | 
			
		||||
        builder.Append("\tprivate static readonly Dictionary<string, GeneratedTransformer> s_transformers = new()\n")
 | 
			
		||||
@@ -104,10 +104,10 @@ public class GeneratedGrammarParser(
 | 
			
		||||
                       """);
 | 
			
		||||
        builder.Append("\n");
 | 
			
		||||
 | 
			
		||||
        builder.Append('\t').Append("public override ITransformer BeginTransformer => ")
 | 
			
		||||
        builder.Append('\t').Append("public ITransformer BeginTransformer => ")
 | 
			
		||||
            .Append($"s_transformers[\"{beginState}\"];\n");
 | 
			
		||||
 | 
			
		||||
        builder.Append('\t').Append("public override NonTerminator Begin => ")
 | 
			
		||||
        builder.Append('\t').Append("public NonTerminator Begin => ")
 | 
			
		||||
            .Append(begin.GenerateCode()).Append(";\n");
 | 
			
		||||
 | 
			
		||||
        builder.Append("}\n");
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								Canon.Tests/CCodeGeneratorTests/BasicTests.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Canon.Tests/CCodeGeneratorTests/BasicTests.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
using Canon.Core.Abstractions;
 | 
			
		||||
using Canon.Core.CodeGenerators;
 | 
			
		||||
using Canon.Core.LexicalParser;
 | 
			
		||||
using Canon.Core.SyntaxNodes;
 | 
			
		||||
using Canon.Tests.GeneratedParserTests;
 | 
			
		||||
 | 
			
		||||
namespace Canon.Tests.CCodeGeneratorTests;
 | 
			
		||||
 | 
			
		||||
public class BasicTests
 | 
			
		||||
{
 | 
			
		||||
    private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance;
 | 
			
		||||
 | 
			
		||||
    [Fact]
 | 
			
		||||
    public void ProgramStructTest()
 | 
			
		||||
    {
 | 
			
		||||
        CCodeBuilder builder = new();
 | 
			
		||||
 | 
			
		||||
        const string program = """
 | 
			
		||||
                               program DoNothing;
 | 
			
		||||
                               begin
 | 
			
		||||
                               end.
 | 
			
		||||
                               """;
 | 
			
		||||
 | 
			
		||||
        Lexer lexer = new(program);
 | 
			
		||||
        List<SemanticToken> tokens = lexer.Tokenize();
 | 
			
		||||
        tokens.Add(SemanticToken.End);
 | 
			
		||||
 | 
			
		||||
        ProgramStruct root = _parser.Analyse(tokens);
 | 
			
		||||
        root.GenerateCCode(builder);
 | 
			
		||||
 | 
			
		||||
        Assert.Equal("#include <PascalCoreLib.h>", builder.Build());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -11,7 +11,7 @@ public class GenerateParserTests
 | 
			
		||||
        Generators = PascalGrammar.Grammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    private readonly GrammarParserBase _parser;
 | 
			
		||||
    private readonly IGrammarParser _parser;
 | 
			
		||||
 | 
			
		||||
    public GenerateParserTests()
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -9,7 +9,7 @@ namespace Canon.Tests.GrammarParserTests;
 | 
			
		||||
 | 
			
		||||
public class PascalGrammarTests
 | 
			
		||||
{
 | 
			
		||||
    private readonly GrammarParserBase _parser = GeneratedGrammarParser.Instance;
 | 
			
		||||
    private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance;
 | 
			
		||||
 | 
			
		||||
    [Fact]
 | 
			
		||||
    public void DoNothingTest()
 | 
			
		||||
@@ -24,8 +24,9 @@ public class PascalGrammarTests
 | 
			
		||||
        List<SemanticToken> tokens = lexer.Tokenize();
 | 
			
		||||
        tokens.Add(SemanticToken.End);
 | 
			
		||||
 | 
			
		||||
        ProgramStruct root = _parser.Analyse(tokens).Convert<ProgramStruct>();
 | 
			
		||||
        ProgramStruct root = _parser.Analyse(tokens);
 | 
			
		||||
        Assert.Equal("DoNothing", root.Head.ProgramName.LiteralValue);
 | 
			
		||||
        Assert.Equal(15, root.Count());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [Fact]
 | 
			
		||||
@@ -43,7 +44,7 @@ public class PascalGrammarTests
 | 
			
		||||
        List<SemanticToken> tokens = lexer.Tokenize();
 | 
			
		||||
        tokens.Add(SemanticToken.End);
 | 
			
		||||
 | 
			
		||||
        ProgramStruct root = _parser.Analyse(tokens).Convert<ProgramStruct>();
 | 
			
		||||
        ProgramStruct root = _parser.Analyse(tokens);
 | 
			
		||||
        Assert.Equal("Add", root.Head.ProgramName.LiteralValue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -63,11 +64,11 @@ public class PascalGrammarTests
 | 
			
		||||
        List<SemanticToken> tokens = lexer.Tokenize();
 | 
			
		||||
        tokens.Add(SemanticToken.End);
 | 
			
		||||
 | 
			
		||||
        ProgramStruct root = _parser.Analyse(tokens).Convert<ProgramStruct>();
 | 
			
		||||
        ProgramStruct root = _parser.Analyse(tokens);
 | 
			
		||||
        Assert.Equal("exFunction", root.Head.ProgramName.LiteralValue);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static GrammarParserBase GenerateGrammarParser()
 | 
			
		||||
    private static IGrammarParser GenerateGrammarParser()
 | 
			
		||||
    {
 | 
			
		||||
        GrammarBuilder builder = new()
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -159,7 +159,7 @@ public class SimpleGrammarWithEmptyTests(ITestOutputHelper testOutputHelper)
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Grammar grammar = builder.Build();
 | 
			
		||||
        GrammarParserBase parser = grammar.ToGrammarParser();
 | 
			
		||||
        IGrammarParser parser = grammar.ToGrammarParser();
 | 
			
		||||
 | 
			
		||||
        ITransformer transformer1 = parser.BeginTransformer;
 | 
			
		||||
        Assert.Equal(3, transformer1.ShiftTable.Count);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user