10 KiB
10 KiB
Tiny C Compiler in C# - Technical Design
Feature Name: tiny-c-compiler-csharp Updated: 2026-05-20
Description
本项目实现一个使用 C# 编写的 C 语言编译器,参考 TCC 的设计理念。编译器将 C 源代码直接编译为 x86/x64 本地机器码,不经过 MSIL 中间层。项目目标是实现一个轻量级、编译速度快的 C 编译器,支持 C99 标准的核心子集。
Architecture
graph TD
A["C Source Code\n.c files"] --> B["Preprocessor\n预处理"]
B --> C["Lexer\n词法分析"]
C --> D["Parser\n语法分析"]
D --> E["Semantic Analyzer\n语义分析"]
E --> F["IR Generator\n中间表示生成"]
F --> G["Code Generator\n代码生成 x86/x64"]
G --> H["Object File\nPE/ELF"]
H --> I["Executable\n可执行文件"]
J["Error Handler"] -.-> B
J -.-> C
J -.-> D
J -.-> E
J -.-> F
J -.-> G
K["Symbol Table"] -.-> E
K -.-> F
K -.-> G
L["Type System"] -.-> E
L -.-> F
Architecture Overview
编译器采用传统的单遍或多遍编译架构,分为以下主要阶段:
- 预处理阶段:处理宏展开、条件编译、头文件包含
- 前端阶段:词法分析、语法分析、语义分析
- 中间阶段:IR 生成与优化
- 后端阶段:x86/x64 代码生成与可执行文件输出
Components and Interfaces
1. Preprocessor (预处理器)
职责:
- 处理
#include指令,展开头文件 - 处理
#define宏定义和宏展开 - 处理条件编译
#ifdef、#ifndef、#endif、#if - 处理
#pragma指令
接口:
public interface IPreprocessor
{
string Preprocess(string sourceCode, string sourceFile);
void AddIncludePath(string path);
void DefineMacro(string name, string? value);
}
2. Lexer (词法分析器)
职责:
- 将预处理后的源代码分解为 token 流
- 识别关键字、标识符、字面量、运算符、分隔符
- 跳过注释和空白
- 报告词法错误
接口:
public interface ILexer
{
IEnumerable<Token> Tokenize(string source);
}
public enum TokenType
{
Keyword, Identifier, IntLiteral, FloatLiteral,
CharLiteral, StringLiteral, Operator, Separator,
EOF, Error
}
public record Token(
TokenType Type,
string Lexeme,
object? Value,
SourceLocation Location
);
3. Parser (语法分析器)
职责:
- 实现递归下降解析器
- 构建抽象语法树(AST)
- 处理 C 语言的运算符优先级
- 报告语法错误
接口:
public interface IParser
{
AstNode Parse();
}
public abstract record AstNode(SourceLocation Location);
public record ProgramNode(List<DeclarationNode> Declarations, SourceLocation Location) : AstNode(Location);
public record FunctionDeclarationNode(
TypeNode ReturnType,
string Name,
List<ParameterNode> Parameters,
BlockStatementNode Body,
SourceLocation Location
) : AstNode(Location);
4. Semantic Analyzer (语义分析器)
职责:
- 类型检查与类型推断
- 符号表管理
- 作用域管理
- 语义错误报告
接口:
public interface ISemanticAnalyzer
{
void Analyze(AstNode root);
}
public interface ISymbolTable
{
void EnterScope();
void ExitScope();
void AddSymbol(string name, Symbol symbol);
Symbol? Lookup(string name);
}
5. IR Generator (中间表示生成器)
职责:
- 将 AST 转换为三地址码形式的 IR
- 构建控制流图(CFG)
- 支持基本优化(常量折叠、死代码消除)
接口:
public interface IIrGenerator
{
IrProgram Generate(AstNode ast);
}
public record IrProgram(List<IrFunction> Functions);
public record IrFunction(string Name, List<IrBasicBlock> BasicBlocks);
public record IrBasicBlock(string Label, List<IrInstruction> Instructions);
public abstract record IrInstruction;
6. Code Generator (代码生成器)
职责:
- 将 IR 转换为 x86/x64 机器码
- 寄存器分配
- 栈帧管理
- 遵循平台调用约定
接口:
public interface ICodeGenerator
{
byte[] Generate(IrProgram program, TargetArchitecture architecture);
}
public enum TargetArchitecture { X86, X64 }
7. Object File Writer (目标文件写入器)
职责:
- 生成 PE 格式文件(Windows)
- 生成 ELF 格式文件(Linux)
- 处理重定位信息
- 设置入口点
接口:
public interface IObjectFileWriter
{
byte[] WriteExecutable(byte[] machineCode, TargetPlatform platform);
}
public enum TargetPlatform { WindowsX86, WindowsX64, LinuxX86, LinuxX64 }
8. Compiler Driver (编译器驱动)
职责:
- 协调各个编译阶段
- 处理命令行参数
- 错误汇总与报告
- 管理编译流程
接口:
public class CompilerDriver
{
public int Run(string[] args);
public CompilationResult Compile(CompilationOptions options);
}
Data Models
Token 模型
public readonly struct SourceLocation
{
public string FileName { get; }
public int Line { get; }
public int Column { get; }
}
public enum TokenType
{
// 关键字
Int, Char, Float, Double, Long, Short, Void,
If, Else, While, For, Do, Switch, Case, Default,
Break, Continue, Return, Struct, Union, Typedef,
// 字面量
IntLiteral, FloatLiteral, CharLiteral, StringLiteral,
// 标识符
Identifier,
// 运算符
Plus, Minus, Star, Slash, Percent,
Equal, NotEqual, Less, Greater, LessEqual, GreaterEqual,
Assign, PlusAssign, MinusAssign, StarAssign, SlashAssign,
And, Or, Not, BitAnd, BitOr, BitXor,
LeftShift, RightShift,
// 分隔符
LeftParen, RightParen, LeftBrace, RightBrace,
LeftBracket, RightBracket,
Semicolon, Comma, Dot, Arrow, Colon,
// 预处理器
HashInclude, HashDefine, HashIf, HashIfdef, HashIfndef, HashElse, HashEndif,
// 特殊
EOF, Error
}
Type System 模型
public abstract record CType(string Name);
public record PrimitiveType(TypeKind Kind) : CType(Kind.ToString())
{
public enum TypeKind { Void, Char, Short, Int, Long, Float, Double }
}
public record PointerType(CType BaseType) : CType($"{BaseType}*");
public record ArrayType(CType ElementType, int Size) : CType($"{ElementType}[{Size}]");
public record StructType(string Name, List<FieldDeclaration> Fields) : CType(Name);
public record FunctionType(CType ReturnType, List<CType> ParameterTypes) : CType("function");
IR 指令模型
public abstract record IrInstruction;
public record IrBinaryOp(IrTemp Dest, IrBinaryOpType Op, IrValue Left, IrValue Right) : IrInstruction;
public record IrUnaryOp(IrTemp Dest, IrUnaryOpType Op, IrValue Source) : IrInstruction;
public record IrLoad(IrTemp Dest, IrValue Address) : IrInstruction;
public record IrStore(IrValue Address, IrValue Value) : IrInstruction;
public record IrCall(IrTemp? Dest, string FunctionName, List<IrValue> Arguments) : IrInstruction;
public record IrJump(string TargetLabel) : IrInstruction;
public record IrBranch(IrValue Condition, string TrueLabel, string FalseLabel) : IrInstruction;
public record IrReturn(IrValue? Value) : IrInstruction;
public record IrLabel(string LabelName) : IrInstruction;
public enum IrBinaryOpType { Add, Sub, Mul, Div, Mod, And, Or, Xor, Shl, Shr, Eq, Ne, Lt, Gt, Le, Ge }
public enum IrUnaryOpType { Neg, Not, BitNot, Deref }
public abstract record IrValue;
public record IrTemp(string Name, CType Type) : IrValue;
public record IrConstant(long Value, CType Type) : IrValue;
public record IrGlobal(string Name, CType Type) : IrValue;
Correctness Properties
不变量
- 类型安全: 所有 IR 指令的操作数类型必须匹配
- 作用域正确性: 符号查找必须遵循词法作用域规则
- 控制流完整性: 所有基本块必须有明确的前驱和后继
- 寄存器一致性: 代码生成前后寄存器状态必须一致
约束条件
- 生成的机器码必须符合 x86/x64 指令集规范
- 函数调用必须遵循目标平台的 ABI(Application Binary Interface)
- 栈帧布局必须保证栈指针对齐(x64 要求 16 字节对齐)
- 可执行文件格式必须符合 PE 或 ELF 规范
Error Handling
错误分类
| 错误类型 | 阶段 | 处理方式 |
|---|---|---|
| 词法错误 | Lexer | 报告错误位置,跳过错误 token |
| 语法错误 | Parser | 报告期望的 token,尝试错误恢复 |
| 类型错误 | Semantic | 报告类型不匹配详情 |
| 未声明符号 | Semantic | 报告符号名称和位置 |
| 代码生成错误 | CodeGen | 报告不支持的 IR 指令 |
错误报告接口
public record ErrorInfo(
ErrorLevel Level, // Warning, Error, Fatal
string Message,
SourceLocation Location,
string? Suggestion = null
);
public interface IErrorReporter
{
void Report(ErrorInfo error);
bool HasErrors { get; }
IEnumerable<ErrorInfo> GetErrors();
}
Test Strategy
单元测试
- Lexer 测试:验证各种 token 的正确识别
- Parser 测试:验证各种 C 语法的 AST 构建
- Semantic 测试:验证类型检查和符号表
- IR 测试:验证 AST 到 IR 的转换
- CodeGen 测试:验证 IR 到机器码的转换
集成测试
- 端到端测试:编译简单 C 程序并验证输出
- 回归测试:使用 TCC 测试套件进行对比测试
- 性能测试:测量编译速度和生成代码质量
测试用例示例
// test_hello.c
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
// test_arithmetic.c
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(3, 4);
return result - 7; // should return 0
}
// test_control_flow.c
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}