parent
f1c569ee0e
commit
67deb0aa2c
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>
|
||||||
/// 语法分析器接口
|
/// 语法分析器接口
|
||||||
/// </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 = [];
|
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>
|
/// </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
|
||||||
|
|
|
@ -21,12 +21,14 @@ public abstract class NonTerminatedSyntaxNode : SyntaxNodeBase, IEnumerable<Synt
|
||||||
{
|
{
|
||||||
yield return child;
|
yield return child;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
NonTerminatedSyntaxNode nonTerminatedNode = child.Convert<NonTerminatedSyntaxNode>();
|
|
||||||
|
|
||||||
foreach (SyntaxNodeBase node in nonTerminatedNode)
|
|
||||||
{
|
{
|
||||||
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;
|
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>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
|
@ -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");
|
||||||
|
|
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)
|
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
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user