Rustで実装されたシンプルなSwiftコンパイラです。このプロジェクトはSwiftプログラミング言語のサブセットに対して、レキサー、パーサー、LLVMコードジェネレータを実装しています。
- レキサー: Swiftソースコードをトークン化
- パーサー: 演算子の優先順位を考慮した抽象構文木(AST)の構築
- LLVMコードジェネレータ: ASTからLLVM IRを生成
- ネイティブ実行: LLVMツールチェーンを使用したコードのコンパイルと実行
- 算術演算: 基本的な四則演算(+、-、*、/)をサポート
- 変数宣言:
letによる変数宣言(型推論付き) - 変数参照: 式内での宣言済み変数の使用
- コマンドラインオプション: 詳細なコンパイル出力のためのverboseモード
cargo buildcargo run -- example/Test.swiftトークン、AST、LLVM IRを含む詳細なコンパイル出力を表示:
cargo run -- --verbose example/Test.swift
# または
cargo run -- -v example/Test.swift--verboseまたは-v: すべてのコンパイル段階を表示するverbose出力を有効化
実行時の処理内容:
- Swiftソースファイルの読み込み
- 入力のトークン化(フロントエンド:字句解析)
- トークンからASTへの解析(フロントエンド:構文解析)
- LLVM IRコードの生成(フロントエンド:IR生成)
- LLVM IRを
target/llvm/output.llに保存 - LLVMツールチェーンを使用したコードの実行(インストール済みの場合)
入力ファイル example/Test.swift:
print(42)
print(42+2)
print(42-2)
print(42*2)
print(42/2)$ cargo run -- example/Test.swift
Source code:
print(42)
print(42+2)
print(42-2)
print(42*2)
print(42/2)
42
44
40
84
21$ cargo run -- --verbose example/Test.swift
Source code:
print(42)
print(42+2)
print(42-2)
print(42*2)
print(42/2)
Tokens:
0: Token { token_type: Print, lexeme: "print" }
1: Token { token_type: LeftParen, lexeme: "(" }
2: Token { token_type: Number, lexeme: "42" }
...
=== AST ===
Program([
Print(Number(42)),
Print(Binary { left: Number(42), operator: Add, right: Number(2) }),
Print(Binary { left: Number(42), operator: Subtract, right: Number(2) }),
Print(Binary { left: Number(42), operator: Multiply, right: Number(2) }),
Print(Binary { left: Number(42), operator: Divide, right: Number(2) }),
])
=== LLVM IR ===
; ModuleID = 'swift_module'
source_filename = "swift_source"
declare i32 @printf(i8*, ...)
@.str = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
define i32 @main() {
entry:
%1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 42)
%2 = add i32 42, 2
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %2)
%4 = sub i32 42, 2
%5 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %4)
%6 = mul i32 42, 2
%7 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %6)
%8 = sdiv i32 42, 2
%9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0), i32 %8)
ret i32 0
}
=== LLVM IR saved to target/llvm/output.ll ===
=== LLVM Execution ===
Execution result: 42
44
40
84
21現在、コンパイラは以下をサポートしています:
print文- 整数リテラル
- 算術式(+、-、*、/)
- 演算子の優先順位を考慮した式の解析
- 抽象構文木(AST)の生成
- Rust 1.56以降
- Cargo
- LLVMツールチェーン(オプション、コード実行用)
brew install llvm
export PATH="/opt/homebrew/opt/llvm/bin:$PATH"sudo apt-get install llvm # Ubuntu/Debian
# または
sudo yum install llvm # RHEL/CentOSビルドせずにコンパイルエラーをチェック:
cargo checkコンパイラはモジュラーアーキテクチャに従っています:
-
フロントエンド(ソース → LLVM IR):
lexer.rs: ソースコードをトークンに変換parser.rs: 演算子の優先順位を考慮してトークンからASTを構築codegen.rs: ASTからLLVM IRを生成
-
バックエンド(LLVM IR → 実行):
llvm_backend.rs: LLVMツールチェーンとの連携を処理- ファイルへのIR保存
- LLVMインタープリタ(lli)による実行
- ネイティブコードへのコンパイル(llc + clang)
-
オーケストレーション:
compiler.rs: コンパイルパイプラインの調整main.rs: CLIインターフェース
コンパイラを実行すると以下のファイルが生成されます:
target/llvm/output.ll- LLVM IRコードtarget/llvm/output.s- アセンブリコード(llc使用時)target/llvm/output- ネイティブ実行可能ファイル(clang使用時)
Apache 2.0