💡
ChatGPTを使ってRustで新しいプログラミング言語をつくり始めた話(if分岐)
前回までのおさらい
前回はPyroに「変数定義」を導入しました。
letprというPyro独自の構文をRustのletに変換できるようにし、変数を使ったプログラムを書けるようにしました。
letpr x = 10
print(x)
今回のテーマ: if分岐
次の一歩として、if分岐を実装しました。
Pyroでは「数値が0以外なら真」というルールで判定しています。
例:
letpr x = 10
if x {
print("x は 0 以外です")
} else {
print("x は 0 です")
}
実装のポイント
- AST (Stmt) に If を追加
pyro/crates/pyroc/src/lib.rs
pub enum Stmt {
Expr(Expr),
Let { name: String, expr: Expr },
+ If {
+ cond: Expr,
+ then_block: Vec<Stmt>,
+ else_block: Vec<Stmt>,
+ },
}
- パーサに if 文を追加
pyro/crates/pyroc/src/parser.rs
match self.at() {
- Tok::Letpr => {
- // (変数定義の処理)
- }
- _ => {
- // (既存の式処理)
- }
+ Tok::If => {
+ self.bump(); // "if"
+ let cond = self.parse_expr()?;
+ let then_block = self.parse_block_stmts()?;
+ let else_block = if matches!(self.at(), Tok::Else) {
+ self.bump();
+ self.parse_block_stmts()?
+ } else {
+ vec![]
+ };
+ Ok(Stmt::If { cond, then_block, else_block })
+ }
+ Tok::Letpr => {
+ // (変数定義の処理)
+ }
+ _ => {
+ // (既存の式処理)
+ }
}
- コード生成に if 分岐を追加
pyro/crates/pyrorts/src/lib.rs
fn generate_stmt(stmt: &Stmt) -> String {
let mut code = String::new();
match stmt {
Stmt::Expr(e) => {
code.push_str(&format!("println!(\"{{}}\", {});\n", expr_to_rust(e)));
}
Stmt::Expr(Expr::Call { callee, args }) if callee == "print" => {
if let Some(arg) = args.get(0) {
code.push_str(&format!("println!(\"{{}}\", {});\n", expr_to_rust(arg)));
} else {
code.push_str("println!(\"<print: missing arg>\");\n");
}
}
Stmt::Expr(_) => { /* no-op */ }
Stmt::Let { name, expr } => {
code.push_str(&format!("let {} = {};\n", name, expr_to_rust(expr)));
}
+ Stmt::If {
+ cond,
+ then_block,
+ else_block,
+ } => {
+ code.push_str(&format!("if ({} as f64) != 0.0 {{\n", expr_to_rust(cond)));
+ for s in then_block {
+ code.push_str(&indent(&generate_stmt(s), 4));
+ }
+ code.push_str("}\n");
+ if !else_block.is_empty() {
+ code.push_str("else {\n");
+ for s in else_block {
+ code.push_str(&indent(&generate_stmt(s), 4));
+ }
+ code.push_str("}\n");
+ }
+ }
}
code
}
実行例
examples/main.pyro
letpr x = 1
letpr y = 0
if x {
print("x は真")
} else {
print("x は偽")
}
if y {
print("y は真")
} else {
print("y は偽")
}
実行手順
プロジェクトルートで以下を実行します。
- ビルド
cargo build
- Rust コードをコンパイル
cargo run -p pyroc-bin -- run
pyro/output/out.rs が生成されます。
生成された Rust コード
pyro/output/out.rs
fn main() {
let x = 1;
let y = 0;
if (x as f64) != 0.0 {
println!("{}", "x は真");
} else {
println!("{}", "x は偽");
}
if (y as f64) != 0.0 {
println!("{}", "y は真");
} else {
println!("{}", "y は偽");
}
}
実行結果:
x は真
y は偽
まとめ
- Pyro に if 分岐 を追加
- 0以外を「真」として判定する仕様
- then/else ブロックを Rust の if/else に変換
これで Pyro は単純な条件分岐を扱えるようになりました 🎉
次は while ループ や 関数定義 に挑戦できそうです。
Discussion