言語のSMIE文法を定義する一般的な方法は、BNFルールの集合を与えることで、優先順位表を保持する新しいグローバル変数を定義することです。例えば、小規模なPascal系言語の文法定義は、次のような形式になります。
(require 'smie) (defvar sample-smie-grammar (smie-prec2->grammar (smie-bnf->prec2
'((id)
(inst ("begin" insts "end")
("if" exp "then" inst "else" inst)
(id ":=" exp)
(exp))
(insts (insts ";" insts) (inst))
(exp (exp "+" exp)
(exp "*" exp)
("(" exps ")"))
(exps (exps "," exps) (exp)))
'((assoc ";"))
'((assoc ","))
'((assoc "+") (assoc "*")))))
いくつか注意点があります:
begin
... endブロックといったsexpの任意の並びが、いずれにせよどこにでも出現することを自動的に許容します。
id
には右辺がありません。これは、空文字列にしかマッチしないという意味ではありません。前述の通り、いずれにせよ任意の sexp
の列がどこにでも出現し得るためです。
";"を代わりに文の区切り記号として扱っており、SMIEはこれを非常にうまく処理できます。
"," や ";" など)は、(foo (foo
"separator" foo) ...)
のようなBNFルールで定義するのが最適です。これにより優先順位の競合が生じますが、明示的に (assoc
"separator") を指定することでその競合を解消できます。
("(" exps ")")ルールは不要でした。このルールが(expsの定義と併せて)果たす役割は、","が括弧の外側に現れてはならないことを明確にすることです。
left や right
を優先すべき十分な理由がない限り、通常は assoc
を用いて演算子を結合的として指定するのが望ましい。そのため、"+" と
"*" は上記では assoc として定義されているが、言語仕様上は左結合として定義されている。