GNU Emacs 32.0.50 (9008bf64c3) のマニュアルを PLaMo 翻訳で機械翻訳したものです。誤訳を含むことがあります。原文: https://www.gnu.org/software/emacs/manual/html_node/elisp/Parsing-Expression-Grammars.html ・ emacs-doc.conao3.com

37 構文表現文法

Emacs Lispには、正規表現(正規表現を参照)から完全な左から右(別名 LL)文法パーサー(Bovineパーサーの開発を参照)まで、テキストの解析やマッチングを行うためのツールがいくつか用意されています。解析式文法(PEG)はテキスト解析のもう一つの手法であり、正規表現よりも構造化されており構成性に優れる一方、文脈自由文法ほどの複雑さはありません。

構文解析式文法(PEG)は、その言語における文字列を認識するためのルール群を用いて形式言語を記述するものです。Emacsでは、PEGパーサーは名前付きルールのリストとして定義され、各ルールはテキストパターンにマッチするか、あるいは他のルールへの参照を含みます。構文解析は関数 peg-run またはマクロ peg-parse(後述)によって開始され、指定されたルール群を用いて現在のバッファ内のポイント以降のテキストを解析します。

PEGにおける各ルールは構文解析式(PEX)と呼ばれ、リテラル文字列、正規表現のような文字範囲や集合、Emacs Lispの関数呼び出しに似たPEG固有の構文、別のルールへの参照、あるいはこれらの組み合わせとして指定できます。文法はルールの木構造として表現され、通常、いずれか一つのルールが「ルート」または「エントリポイント」ルールとして扱われます。例えば:

((number sign digit (* digit))
 (sign   (or "+" "-" ""))
 (digit  [0-9]))

一度定義すれば、文法を用いて現在のバッファ内の指定した位置以降のテキストを、さまざまな方法でパースできます。peg-parse マクロが最もシンプルです:

マクロ: peg-parse &rest pexs

ポイント pexs に一致。

(peg-parse
  (number sign digit (* digit))
  (sign   (or "+" "-" ""))
  (digit  [0-9]))

このマクロはシンプルである一方、ルールをソースコードに直接記述しなければならないため、柔軟性に欠けます。他の関数やマクロを組み合わせて使用することで、より高い柔軟性を得られます。

マクロ: with-peg-rules rules &rest body

body を、PEX のリストである rules を用いて実行する。BODY 内では、peg-run の呼び出しによりパースが開始される。

関数: peg-run peg-matcher &optional failure-function success-function

この関数は、名前付きルール(通常はより大規模な文法のエントリポイント)に対して peg(後述)を呼び出した結果である単一の peg-matcher を受け取ります。

パースの終了時に、パースの成否に応じて failure-function または success-function のいずれかが呼び出されます。success-function が指定されている場合、それはパース中にスタックに蓄積されたすべてのアクションを実行する無名関数を唯一の引数として受け取る関数である必要があります。デフォルトでは、この無名関数は単に実行され、peg-matcher が停止した時点から処理が再開されます。

一方、パースに失敗した場合は、peg-matcher が指し示していた位置にかかわらず、ポインタはパーサーが到達できた最遠の位置まで移動します。これは多くの場合、「エラー箇所」の妥当な推測となります。その後、failure-function として指定された関数が、パース中に失敗した PEG 式のリストを引数として呼び出されます。デフォルトでは、このリストは破棄されます。

peg-run に渡される peg-matcher は、peg の呼び出しによって生成されます:

マクロ: peg &rest pexs

pexsを、peg-runに渡せる単一のペグマッチャーに変換する。

上記の peg-parse の例は、これらの関数への呼び出し群に展開され、全文で記述すると次のようになります。

(with-peg-rules
    ((number sign digit (* digit))
     (sign   (or "+" "-" ""))
     (digit  [0-9]))
  (peg-run (peg number)))

この手法により、パースの「エントリポイント」をより明示的に制御できるほか、異なるソースのルールを組み合わせることも可能になります。

また、マクロ define-peg-rule を用いて、より defun に近い構文で個別のルールを定義することも可能です:

マクロ: define-peg-rule name args &rest pexs

nameを、argsを受け入れ、pointでpexsにマッチするPEGルールとして定義する。

例えば:

(define-peg-rule digit ()
  [0-9])

funcall PEGルールでは、ルールに引数を渡すことができます(PEXの定義を参照)。

別の方法として、define-peg-ruleset を用いて名前付きのルールセットを定義することもできます:

マクロ: define-peg-ruleset name &rest rules

nameをrulesの識別子として定義する。

(define-peg-ruleset number-grammar
  ;; `digit' here references the definition above.
  (number () sign digit (* digit))
  (sign () (or "+" "-" "")))

このように定義されたルールやルールセットは、後続の peg-run や with-peg-rules の呼び出しにおいて名前で参照できます:

(with-peg-rules (number-grammar)
  (peg-run (peg number)))

デフォルトでは、peg-run や peg-parse の呼び出しによる出力はありません。パースは単にポイントを移動させるだけです。パース済みの文字列を返したり、それ以外の処理を行ったりするには、ルールに アクション を含めることができます。詳細は パースアクション を参照してください。