Chapter 03無料公開

Prattパーサの実装①: 前置演算子

井山梃子歴史館
井山梃子歴史館
2020.12.20に更新

対応するコミット:

この章では前置演算子として-をパースします.
Prattパーサの構造を思い出しましょう:

fn parse_expr_bp(input: Input, min_bp: u16) -> Expr {
  let expr = if input.is_leading_op() { // 先行演算子で始まるか?
    // 先行演算子のパース
  } else {
    parse_atomic_expr(input) // アトミックな式(リテラルなど)のパース
  };

  loop {
    // 後続演算子が続かない場合はおしまい
    if !input.is_following_op() {
      return expr;
    }

    expr = {
      // 後続演算子のパース
    };
  }
}

前置演算子は先行演算子なので,一番最初にパースすることになりますね.
入力から一文字読み取って-かどうかチェックすることにします.
もし-だった場合はその分だけ入力を進めてから-の引数を再帰的にパースします.
最終的には-を関数とするS式を構築しておしまいです.
一方,-でない場合はアトミックな式のパースをします.

pub fn parse_expr(input: &mut Input<'_>) -> SExpr {
  match input.peek().unwrap() {
    '-' => {
      input.bump(); // '-'を消費
      let following_expr = parse_expr(input); // 演算子の対象を再帰的にパース
      SExpr::List(vec![SExpr::Atom("-".into()), following_expr])
    }
    _ => parse_atom(input),
  }
}

テストしてみます.

#[test]
fn test_simple_prefix() {
  complete_parse("-8", "(- 8)")
}