Rustのparserを試す

ちょっと必要になったきがするのでparserをかくためのものを探したり
サンプルを書いてみたりした。
個人的なメモ
What is the difference between parser generators and parser combinators? - Quora
compiler construction - What is the difference between LALR and LR parsing? - Stack Overflow
専門と離れているので適当に下調べ。

nom - Rust
combine - Rust
pest. The Elegant Parser
LALRPOP -
syntax-cli - npm

最初はlex(情報がたくさん),bison(情報がたくさん)に似てると書いてある
syntax-cliがいいかもしれないと思ったけど、
パーサーを生成するコマンドがbuild.rsでうまく自動化できなかったのでやめた。

結局最初から生成もセットで提供されている
lalrpopを使うことにした。

とりあえず式をパースするサンプルを書いた。
f:id:katakanan:20190706151544p:plain

use std::str::FromStr;
use crate::ast::Node;

grammar;

NUM_INTEGER: i32 = r"[0-9]+" => i32::from_str(<>).unwrap();
IDENTIFIER: String = r"[a-zA-Z_][a-zA-Z0-9_$]*" => <>.to_string();

pub Expr: Node = {
  <e:Expr> "+" <f:Factor> => Node::Binary{
                                            op: "+",
                                            left: Box::new(e),
                                            right: Box::new(f),
                                          },
  Factor,
};

pub Factor: Node = {
  <f:Factor> "*" <t:Term> => Node::Binary{
                                            op: "*",
                                            left: Box::new(f),
                                            right: Box::new(t),
                                          },
  Term,
};

pub Term: Node = {
  <n:NUM_INTEGER> => Node::Literal(n),
  <ident:IDENTIFIER> => Node::Ident(ident),
  "(" <Expr> ")",
};
#[derive(Debug)]
pub enum Node {
    Literal(i32),
    Ident(String),
    Binary {
        op: &'static str,
        left: Box<Node>,
        right: Box<Node>,
    },
}
#[macro_use]
extern crate lalrpop_util;

pub mod ast;
pub mod parser;

fn main() {
    println!("Hello, world!");
    println!("{:?}", parser::ExprParser::new().parse("((22))"));
    println!("{:?}", parser::ExprParser::new().parse("(a_A0)"));
    println!("{:?}", parser::ExprParser::new().parse("(2+a)*4"));
    println!("{:?}", parser::ExprParser::new().parse("2+a*4"));
    println!("{:?}", parser::ExprParser::new().parse("5*4+b"));
    println!("{:?}", parser::ExprParser::new().parse("5*(4+b)"));
}
    Finished dev [unoptimized + debuginfo] target(s) in 1.68s
     Running `target/debug/calc3`
Hello, world!
Ok(Literal(22))
Ok(Ident("a_A0"))
Ok(Binary { op: "*", left: Binary { op: "+", left: Literal(2), right: Ident("a") }, right: Literal(4) })
Ok(Binary { op: "+", left: Literal(2), right: Binary { op: "*", left: Ident("a"), right: Literal(4) } })
Ok(Binary { op: "+", left: Binary { op: "*", left: Literal(5), right: Literal(4) }, right: Ident("b") })
Ok(Binary { op: "*", left: Literal(5), right: Binary { op: "+", left: Literal(4), right: Ident("b") } })

うーんよさそう?

ある文法をパースしたかったら
既存のプロジェクトを参考にできる
GitHub - RustPython/RustPython: A Python Interpreter written in Rust
GitHub - tcr/rust-verilog: Verilog parsing and generator crate.