PEG.js で機械的に構文を定義したい時に ejs 使ったら便利だった

読了まで:約1分


と​言う​話です。

話の​前提とか​理由

大体、

と​いう​辺りの​問題。

で、​もう​ちょっと​詳し書くと、​今作っている​ nytra と​いう​軽量マークアップ言語 (markdown とかはてな記法とかの​類い​) では​ PEG.js を​使って​構文定義を​していて、​かつ、​インラインの​軽量マークアップ言語を​定義する​際に、

各構文が、​自身の​構文を​除く、​他の​すべての​インライン構文を​子要素と​して​持つ

と​言う​定義を​書かないと​上手く​動かな​そうな​感じが​有りました。

それで、​その​辺りを​上手く​動か​すために、​上記の​構文定義を​したないとなー、と​いう​感じだったんですが、​ その​構文定義を​手作業で​書こうと​すると、​どう​考えても​ミスが​出そうだし、​ あと​同じ​様な​構文を​各構文毎に​定義するのが​正直つらみしか​無さそうだったので、

じゃ、​テンプレート処理すれば​良いじゃん

ってなって、

とりあえず​サクっと​何も​考えずに​使える​ ejs ​使うかー

と​いう​意思決定を​して、​実際​使ってみたら​便利だった、と​いうのが​今回の​話です。

今現在の​処理の​流れ

実際の​パーザの​コードを​生成する​流れと​しては、

  1. ejs を​含んだ​ PEG.js 用の​コードを​書く
  2. ejs の​テンプレート処理を​して​ PEG.js の​コードを​生成する
  3. 生成した​ PEG.js の​コードから​実際の​パーザの​コードを​吐かせる

と​いう​感じに​なっています。​ そして、​下記が​実際に​パーザを​生成する​時に​使ってる​コードです:

"use strict";
const ejs = require("ejs");
const peg = require("pegjs");
const fs = require("fs");
const path = require("path");
"inline.pegjs.ejs block.pegjs.ejs".split(" ").forEach((fn) => {
  fs.readFile(path.join(__dirname, fn), (err, template) => {
    if (err !== null) {
      throw err;
    }
    var source = ejs.render(template.toString(), {}, {});
    var output = peg.generate(source, { output: "source", format: "commonjs" });
    fs.writeFile(
      path.join(__dirname, "..", "dist", fn.replace(".pegjs.ejs", ".js")),
      output,
      (err) => {
        if (err !== null) {
          throw err;
        }
      }
    );
  });
});

なんか​割と​ザツいですね。

ejs + PEG.js + HAST を​組み合わせると、​軽量マークアップ言語を​作るの​凄く​楽

今現在、​ nytra の​コードは、

の​三つを​組み合わせつつ​書いているのですが、HAST​ と​その​周辺ツールの​おかげで、​ Abstract Syntax Tree を​自前で​用意しなくて​済んでいるし、​あと、​ HAST から​ HTML に​変換するのも​ HAST の​周辺ツールを​使うだけで​生成できちゃってるので、​ そう​言った​意味合いでは、

  • ejs + PEG.js + HAST

と​いう​組み合わせを​見付けられた、と​いうのは、​今回凄く​良かったかなーとか​思っています。​はい。

以上

なんか​今日は​割と​ザツに​記事書いてますが、​大体​そんな​感じでした。​はい。

にゃるら(カラクリスタ)

『輝かしい青春』なんて失かった人。
次に備えて待機中。

今は趣味でプログラミングをして
生活しています。