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>
/// 语法分析器接口 /// 语法分析器接口
/// </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<AnalyseState> stack = [];
stack.Push(new AnalyseState(BeginTransformer, SyntaxNodeBase.Create(SemanticToken.End))); 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 = []; 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> /// </summary>
public required LrState BeginState { get; init; } public required LrState BeginState { get; init; }
public GrammarParserBase ToGrammarParser() public IGrammarParser ToGrammarParser()
{ {
Dictionary<LrState, Transformer> transformers = []; Dictionary<LrState, Transformer> transformers = [];
@ -71,10 +71,10 @@ public class Grammar
return new GrammarParser(transformers[BeginState], Begin); 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 ITransformer BeginTransformer { get; } = beginTransformer;
public override NonTerminator Begin { get; } = begin; public NonTerminator Begin { get; } = begin;
} }
private class Transformer : ITransformer private class Transformer : ITransformer

View File

@ -21,7 +21,8 @@ public abstract class NonTerminatedSyntaxNode : SyntaxNodeBase, IEnumerable<Synt
{ {
yield return child; yield return child;
} }
else
{
NonTerminatedSyntaxNode nonTerminatedNode = child.Convert<NonTerminatedSyntaxNode>(); NonTerminatedSyntaxNode nonTerminatedNode = child.Convert<NonTerminatedSyntaxNode>();
foreach (SyntaxNodeBase node in nonTerminatedNode) foreach (SyntaxNodeBase node in nonTerminatedNode)
@ -30,6 +31,7 @@ public abstract class NonTerminatedSyntaxNode : SyntaxNodeBase, IEnumerable<Synt
} }
} }
} }
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
} }

View File

@ -1,4 +1,5 @@
using Canon.Core.Enums; using Canon.Core.CodeGenerators;
using Canon.Core.Enums;
namespace Canon.Core.SyntaxNodes; namespace Canon.Core.SyntaxNodes;
@ -20,4 +21,9 @@ public class ProgramStruct : NonTerminatedSyntaxNode
{ {
return new ProgramStruct { Children = children }; 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; using Canon.Core.LexicalParser;
namespace Canon.Core.SyntaxNodes; namespace Canon.Core.SyntaxNodes;
public abstract class SyntaxNodeBase public abstract class SyntaxNodeBase : ICCodeGenerator
{ {
public abstract bool IsTerminated { get; } public abstract bool IsTerminated { get; }
@ -19,6 +21,14 @@ public abstract class SyntaxNodeBase
return result; return result;
} }
/// <summary>
/// 语法树节点基类对于生成C代码的默认实现
/// </summary>
public virtual void GenerateCCode(CCodeBuilder builder)
{
}
public static SyntaxNodeBase Create(SemanticToken token) public static SyntaxNodeBase Create(SemanticToken token)
{ {
return new TerminatedSyntaxNode { Token = token }; return new TerminatedSyntaxNode { Token = token };

View File

@ -7,11 +7,11 @@ namespace Canon.Generator.GrammarGenerator;
public class GeneratedGrammarParser( public class GeneratedGrammarParser(
Dictionary<string, GeneratedTransformer> transformers, Dictionary<string, GeneratedTransformer> transformers,
string beginState, 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) public string GenerateCode(string namespaceValue)
{ {
@ -74,7 +74,7 @@ public class GeneratedGrammarParser(
"""); """);
builder.Append('\n'); builder.Append('\n');
builder.Append("public class GeneratedGrammarParser : GrammarParserBase\n") builder.Append("public class GeneratedGrammarParser : IGrammarParser\n")
.Append("{\n"); .Append("{\n");
builder.Append("\tprivate static readonly Dictionary<string, GeneratedTransformer> s_transformers = new()\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("\n");
builder.Append('\t').Append("public override ITransformer BeginTransformer => ") builder.Append('\t').Append("public ITransformer BeginTransformer => ")
.Append($"s_transformers[\"{beginState}\"];\n"); .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"); .Append(begin.GenerateCode()).Append(";\n");
builder.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) Generators = PascalGrammar.Grammar, Begin = new NonTerminator(NonTerminatorType.StartNonTerminator)
}; };
private readonly GrammarParserBase _parser; private readonly IGrammarParser _parser;
public GenerateParserTests() 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 public class PascalGrammarTests
{ {
private readonly GrammarParserBase _parser = GeneratedGrammarParser.Instance; private readonly IGrammarParser _parser = GeneratedGrammarParser.Instance;
[Fact] [Fact]
public void DoNothingTest() public void DoNothingTest()
@ -24,8 +24,9 @@ public class PascalGrammarTests
List<SemanticToken> tokens = lexer.Tokenize(); List<SemanticToken> tokens = lexer.Tokenize();
tokens.Add(SemanticToken.End); 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("DoNothing", root.Head.ProgramName.LiteralValue);
Assert.Equal(15, root.Count());
} }
[Fact] [Fact]
@ -43,7 +44,7 @@ public class PascalGrammarTests
List<SemanticToken> tokens = lexer.Tokenize(); List<SemanticToken> tokens = lexer.Tokenize();
tokens.Add(SemanticToken.End); tokens.Add(SemanticToken.End);
ProgramStruct root = _parser.Analyse(tokens).Convert<ProgramStruct>(); ProgramStruct root = _parser.Analyse(tokens);
Assert.Equal("Add", root.Head.ProgramName.LiteralValue); Assert.Equal("Add", root.Head.ProgramName.LiteralValue);
} }
@ -63,11 +64,11 @@ public class PascalGrammarTests
List<SemanticToken> tokens = lexer.Tokenize(); List<SemanticToken> tokens = lexer.Tokenize();
tokens.Add(SemanticToken.End); tokens.Add(SemanticToken.End);
ProgramStruct root = _parser.Analyse(tokens).Convert<ProgramStruct>(); ProgramStruct root = _parser.Analyse(tokens);
Assert.Equal("exFunction", root.Head.ProgramName.LiteralValue); Assert.Equal("exFunction", root.Head.ProgramName.LiteralValue);
} }
private static GrammarParserBase GenerateGrammarParser() private static IGrammarParser GenerateGrammarParser()
{ {
GrammarBuilder builder = new() GrammarBuilder builder = new()
{ {

View File

@ -159,7 +159,7 @@ public class SimpleGrammarWithEmptyTests(ITestOutputHelper testOutputHelper)
}; };
Grammar grammar = builder.Build(); Grammar grammar = builder.Build();
GrammarParserBase parser = grammar.ToGrammarParser(); IGrammarParser parser = grammar.ToGrammarParser();
ITransformer transformer1 = parser.BeginTransformer; ITransformer transformer1 = parser.BeginTransformer;
Assert.Equal(3, transformer1.ShiftTable.Count); Assert.Equal(3, transformer1.ShiftTable.Count);