🐈

言語処理系のツボ:制御フロー (WIP)

2024/10/01に公開

『コンパイラ 原理・技法・ツール(第 2 版)』の、制御フローに関する部分の基本をおさえた以下の短いコードについて、説明する記事を書きます(予定)

fn main() {
    let input = "
      a b c
      if x
        d e
      else
        f g
      end
      h i j
      if y
        k l
        if z
          m
        else
          n o
        end
        p q
      end
      r s t
      while x
        u v
      end
      w
    end";
    let mut words = input.split_whitespace();
    let mut stmts = Vec::new();
    parse(&mut words, &mut stmts, None);

    for stmt in &stmts {
        stmt.translate();
    }
    println!("End.");
}

enum Stmt {
    Expr(String),
    If(String, Vec<Stmt>, Vec<Stmt>),
    While(String, Vec<Stmt>),
    Break,
    Continue,
}

fn parse<'a>(
    words: &mut impl Iterator<Item = &'a str>,
    stmts: &mut Vec<Stmt>,
    stmts_else: Option<&mut Vec<Stmt>>,
) {
    loop {
        match words.next().unwrap() {
            "if" => stmts.push({
                let cond = words.next().unwrap();
                let mut stmts_then = Vec::new();
                let mut stmts_else = Vec::new();
                parse(words, &mut stmts_then, Some(&mut stmts_else));
                Stmt::If(String::from(cond), stmts_then, stmts_else)
            }),
            "else" => return parse(words, stmts_else.unwrap(), None),
            "while" => stmts.push({
                let cond = words.next().unwrap();
                let mut stmts = Vec::new();
                parse(words, &mut stmts, None);
                Stmt::While(String::from(cond), stmts)
            }),
            "break" => stmts.push(Stmt::Break),
            "continue" => stmts.push(Stmt::Continue),
            "end" => return,
            expr => stmts.push(Stmt::Expr(String::from(expr))),
        }
    }
}

impl Stmt {
    fn translate(&self) {
        match self {
            Stmt::Expr(expr) => print!("{expr}, "),
            Stmt::If(cond, stmts_then, stmts_else) => {
                println!("Br {cond}.");
                for stmt in stmts_then {
                    stmt.translate();
                }
                println!("Jmp.");
                for stmt in stmts_else {
                    stmt.translate();
                }
                println!("Jmp.");
            }
            Stmt::While(cond, stmts) => {
                println!("Jmp.");
                println!("Br {cond}.");
                for stmt in stmts {
                    stmt.translate();
                }
                println!("Jmp.");
            }
            Stmt::Break | Stmt::Continue => println!("Jmp."),
        }
    }
}

Discussion