refact: 将编译中各个阶段接口化 (#35)

Reviewed-on: PostGuard/Canon#35
This commit is contained in:
jackfiled 2024-04-12 19:01:37 +08:00
parent f1c569ee0e
commit 67deb0aa2c
15 changed files with 748 additions and 615 deletions

View File

@ -0,0 +1,11 @@
using Canon.Core.CodeGenerators;
namespace Canon.Core.Abstractions;
/// <summary>
/// 支持生成C语言代码的接口
/// </summary>
public interface ICCodeGenerator
{
public void GenerateCCode(CCodeBuilder builder);
}

View File

@ -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 = [];

View File

@ -0,0 +1,11 @@
using Canon.Core.LexicalParser;
namespace Canon.Core.Abstractions;
/// <summary>
/// 词法分析器接口
/// </summary>
public interface ILexer
{
public IEnumerable<SemanticToken> Tokenize(ISourceReader reader);
}

View 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; }
}

View 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();
}
}

View File

@ -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

View File

@ -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;
}
}
}
}

View File

@ -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>");
}
}

View File

@ -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 };

View File

@ -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");

View 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());
}
}

View File

@ -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

View File

@ -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()
{

View File

@ -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);