先日 markdown-to-ast を使って markdown をパースするというエントリーを書いたのですが、 markdown-to-ast は commonmark をラップしたライブラリだとの情報を得た。
それならば、markdown-to-ast を使うのではなく、直接 commonmark を使ってみることにする。
$ mkdir m-to-ast
$ cd m-to-ast
$ npm init -y
依存するライブラリをインストール:
$ npm install commonmark
$ npm install underscore
index.js を書く:
const commonmark = require("./node_modules/commonmark/dist/commonmark.js");
const reader = new commonmark.Parser();
const ast = reader.parse("Hello, *World*!");
console.log(ast);
実行する:
$ node index.js
標準出力(長いため先頭のみ):
<ref *1> Node {
_type: 'document',
_parent: null,
_firstChild: <ref *2> Node {
_type: 'paragraph',
_parent: [Circular *1],
_firstChild: Node {
_type: 'text',
_parent: [Circular *2],
_firstChild: null,
markdown-to-ast と似たもの(というか本来は、markdown-to-ast が commonmark の出力に似せたのであろう)が出ます。 ただ、markdown-to-ast では、サブノード(s) を children で取得できたのですが、 commonmark では、children という属性はありません。 その代わり、 _firstChild があり _next があります。 この二つの属性を使うことで、markdown-to-ast における children を作り出すことができます。
このように:
const children = (node) => {
const recur = (acc, child) => {
if( child==null ){
return acc;
}
else {
acc.push(child);
return recur(acc, child._next);
}
};
return recur([], node._firstChild);
};
要するに、_firstChild で先頭のサブノードを取得し、その後はそのサブノードの _next を見て、(兄弟のノードが)存在していれば、処理を続ける。以下同様に _next を見て兄弟を探し続ける。という再帰処理です。
あとはだいたい 前回(markdow-to-ast編) と同じです。
完成したコードはこちら:
const commonmark = require("./node_modules/commonmark/dist/commonmark.js");
const _ = require('underscore');
const children = (node) => {
const recur = (acc, child) => {
if( child==null ){
return acc;
}
else {
acc.push(child);
return recur(acc, child._next);
}
};
return recur([], node._firstChild);
};
const parseNode = (node) => {
// _.foldl で使う関数:
const f = (acc, subNode) => {
return acc + parseNode(subNode);
};
if(node._type == 'document' ){
return '<doc>' + _.foldl(children(node), f, '') + '</doc>';
}
else if(node._type == 'paragraph' ){
return '<p>' + _.foldl(children(node), f, '') + '</p>';
}
else if(node._type == 'emph' ){
return '<emp>' + _.foldl(children(node), f, '') + '</emp>';
}
else if(node._type == 'text' ){
return '<str>' + node._literal + '</str>';
}
};
const reader = new commonmark.Parser();
const ast = reader.parse("Hello, *World*!");
const xml = parseNode(ast);
console.log(xml);
実行と結果:
$ node index.js
<doc><p><str>Hello, </str><emp><str>World</str></emp><str>!</str></p></doc>