自作言語の構文を考えるのがめんどくさいそこのアナタ、Rustのマクロで楽してもいいわよ?
最近 とっくんのYouTubeチャンネル - YouTube さんの再度ハマり言語野が支配されてます、id:Krouton です。
事の発端
頭の中の大蛇丸が「今日はexpression-orientedな言語を作っていくことにするわね」「構文を考えるのがめんどくさいそこのアナタ、AST設計からスタートしても良くてよ?」って言ってくる
— 初診15分 (@Krout0n) 2020年5月7日
つまり「言語を作りたいけど具象構文考えるのめんどくせ〜〜!!!!」「パーサジェネレータ使うにしてもそれすら書き下すのもめんどくせ〜〜!!!!!」って時ありますよね?(あってくれ)
そんなアナタのためにS式でASTを表現するのをオススメします。Rustが書きたかったのでそうしましたが、他のマクロがサポートされてる言語なら大体できそう?
TL;DR と まとめ
- (1 + 2) + (3 + 4) を表現するのに
BinOP( Box::new(BinOP(Box::new(Number(1)), Add, Box::new(Number(2)))), Add, Box::new(BinOP(Box::new(Number(3)), Add, Box::new(Number(4)))), )
から
ast!((+ (+ 1 2) (+ 3 4)))
という風にS式で表現できるようになる。Box::newを手書きするのから解放されるようになる。
本編
type AST = Expression; #[derive(Debug, PartialEq)] pub enum Expression { Number(usize), Ident(String), } use Expression::*; impl From<usize> for Expression { fn from(n: usize) -> Self { Number(n) } } impl<'a> From<&'a str> for Expression { fn from(s: &'a str) -> Expression { Ident(s.to_string()) } } macro_rules! ast { ($i:ident) => { Expression::from(stringify!($i)) }; ($e:expr) => { Expression::from($e) }; } fn main() { dbg!(ast!(2)); // Number(2) dbg!(ast!(id)); // Ident("id") }
ここはFromを使ってるだけで特に難しいところはありませんね。ASTとマクロを拡張して関数適用(Apply)を考えてみましょう。
#[derive(Debug, PartialEq)] pub enum Expression { // 略 Apply { fn_lit: Box<Expression>, args: Vec<Expression>, }, } macro_rules! ast { (($fn:tt $( $arg:tt )*)) => { Apply { fn_lit: Box::new(ast!($fn)), args: vec![$( ast!($arg), )*] } }; // 略 ($e:expr) => { Expression::from($e) }; // exprより後にしないと1 や 2が全てIdentになるので最後に置く ($t:tt) => { Expression::from(stringify!($t)) } } ast!((print 1 2)); // Apply { fn_lit: Ident("print"), args: [Number(1), Number(2)] } ast!((+ 1 2)); // Apply { fn_lit: Ident("+"), args: [Number(1), Number(2)] }
ミソとしては四則演算を関数適用として、stringify!で+などの演算子をIdentとして解釈する部分です。これのおかげで、四則演算用のマクロのルールを定義せずに表現できるようになりました。
あとは好きな風にASTを拡張して好きなS式で表現しましょう!
拡張したgist gist.github.com
Special Thanks
- S-expression in Rust macro_rules - Stack Overflow *2 に答えていただいたDenisKolodinさん
- この場を借りて感謝を申し上げます。
- とっくんさん
*1: コンパイラのミドルエンドって滅多に使わない言葉らしいってWikipedia先生が言ってた コンパイラ - Wikipedia
*2: 質問者は私です