ククログ

株式会社クリアコード > ククログ > RedPenのValidatorをJavaScript で書くには

RedPenのValidatorをJavaScript で書くには

はじめに

RedPen という技術文書の校正に使うことを目的としたソフトウェアがあります。

コマンドラインから RedPen を起動して文章をチェックしたり、RedPen サーバを稼動させ、REST API 経由で使うこともできます。

RedPen そのものの詳しい説明は、開発者による連載記事があるのでそちらを参照するとよいでしょう。

今回は、RedPen 1.3 からサポートされた、JavaScript による Validator を書く方法を紹介します。

RedPen のセットアップ

RedPen の リリースページ からこの記事を書いている時点の最新版である RedPen 1.3 をダウンロードします。

ダウンロードできたら任意のディレクトリに展開します。RedPen を利用するには Java 8 の実行環境をあらかじめセットアップしておきます。

以下のように redpen コマンドを正常に実行できたら準備は完了です。

% ./redpen-distribution-1.3/bin/redpen -v
1.3.0

RedPen で文章をチェックする

RedPen で文章をチェックするには、設定ファイル(XML)と対象となるテキストを用意します。

設定ファイルには、どのルール(Validator)を使って対象となる文章をチェックするかを指定します。 設定ファイルを指定しなかった場合には、デフォルトのルールが適用されます。

配布物にサンプルのテキストが含まれているので、それを使ってどんなふうにチェックできるかがわかります。

% ./redpen-distribution-1.3/bin/redpen redpen-distribution-1.3/sample-doc/ja/sampledoc-ja.md

例えば、上記のテキストだと次のようなエラー(一部抜粋)を検出できます。

sampledoc-ja.md:2: ValidationError[SentenceLength], 文長("101")が最大値 "100" を超えています。 at line: 最近利用されているソフトウェアの中には複数の計算機上で動作(分散)するものが多く存在し、このような分散ソフトウェアは複数の計算機で動作することで大量のデータを扱えたり、高負荷な状況に対処できたりします。
sampledoc-ja.md:3: ValidationError[InvalidSymbol], 不正なシンボル "," がみつかりました。 at line: 本稿では,複数の計算機(クラスタ)でで動作する各サーバーを**インスタンス**と呼びまます。
sampledoc-ja.md:3: ValidationError[KatakanaEndHyphen], カタカナ単語 "サーバー" に不正なハイフンが見つかりました。 at line: 本稿では,複数の計算機(クラスタ)でで動作する各サーバーを**インスタンス**と呼びまます。aEndHyphen],

Validator を書くには

RedPen の使い方がわかったところで、今度は実際に Validator を書いてみましょう。

Validator の配置先

Validator はどこに実装したものを配置すればいいのでしょうか。RedPen で Validator の置き場所を指定するには、2つ方法があります。

  • REDPEN_HOME 環境変数で配置先のパスを指定する

  • 設定ファイルで JavaScriptValidatorscript-path で配置先のパスを指定する

REDPEN_HOME を指定するときは、実際には $REDPEN_HOME/js 以下から RedPen が Validator を探します。

Validator で実装すべきもの

Validator で実装すべきものについては、RedPen のソースコードのコメントにサンプルがあります。

var message = "your sentence has validation error : {0}";
function preValidateSentence(sentence) {
}
function preValidateSection(section) {
}
function validateDocument(document) {
  // your validation logic for document here
}
function validateSentence(sentence) {
  // if(your validation logic for sentence here) {
  //   addValidationError(sentence, 'specific message');
  // }
}
function validateSection(section) {
  // your validation logic for section here
}

いろいろありますが、文のチェックで最低限必要なのは、messagevalidateSentence の実装です。

var message = "your sentence has validation error : {0}";
function validateSentence(sentence) {
  // if(your validation logic for sentence here) {
  //   addValidationError(sentence, 'specific message');
  // }
}

バリデーション処理で問題のある箇所を見付けたら、addValidationError(sentence, 'specific message'); を呼びます。

RedPen と形態素解析

RedPen では、Kuromoji を単語の分割に使用しています。 validateSentence 内で sentence.tokens.forEach(print); などとして分割されたトークン 1 を出力すると、どのように分割されたかがわかります。

例えば、次のテキストがどのように分割されるかみてみましょう。

% cat ranuki.md
# ら抜き言葉

お刺身を食べれない。

次のようなトークンとして分割されていることがわかります。

TokenElement{surface='お', offset=0, tags=[接頭詞, 名詞接続, *, *, *, *, お, オ, オ]}
TokenElement{surface='刺身', offset=1, tags=[名詞, 一般, *, *, *, *, 刺身, サシミ, サシミ]}
TokenElement{surface='を', offset=3, tags=[助詞, 格助詞, 一般, *, *, *, を, ヲ, ヲ]}
TokenElement{surface='食べ', offset=4, tags=[動詞, 自立, *, *, 一段, 未然形, 食べる, タベ, タベ]}
TokenElement{surface='れ', offset=6, tags=[動詞, 接尾, *, *, 一段, 未然形, れる, レ, レ]}
TokenElement{surface='ない', offset=7, tags=[助動詞, *, *, *, 特殊・ナイ, 基本形, ない, ナイ, ナイ]}
TokenElement{surface='。', offset=9, tags=[記号, 句点, *, *, *, *, 。, 。, 。]}

surface が実際に分割された文字で、Kuromojiによる形態素解析結果を tags に保持していることがわかります。

「ら抜き言葉」をチェックする

テキストがどのように分割されるのか、そして形態素解析の結果がどのように保持されているかがわかったところで、いわゆる「ら抜き言葉」をチェックしてみましょう。 厳密なチェックまでは踏み込まず、一段活用の未然形に対する接尾というとても簡略なチェックだけをします。厳密ではないので誤検出もありますが、Validator のサンプルなのでそれでよいものとします。

具体的な実装例は次のとおりです。

var message = "your sentence has validation error : {0}";

function isTargetVerb(token) {
  if (token.tags[0] == '動詞' &&
    token.tags[1] == '自立' &&
    token.tags[4] == '一段' &&
    token.tags[5] == '未然形') {
    return true;
  } else {
    return false;
  }
}

function isRaRemovedWord(token, token2) {
  if (isTargetVerb(token) &&
    token2.tags[0] == '動詞' &&
    token2.tags[1] == '接尾' &&
    token2.tags[6] == 'れる') {
    return true;
  } else {
    return false;
  }
}

function validateSentence(sentence) {
  for (var i = 0; i < sentence.tokens.length; i++) {
    if (i + 1 < sentence.tokens.length &&
      isRaRemovedWord(sentence.tokens[i],
                      sentence.tokens[i + 1])) {
      addValidationError(sentence, 'ら抜き言葉を使用しています。');
      //sentence.tokens.forEach(print);
    }
  }
}

こうして実装した Validator を試してみましょう。JavaScriptValidator を使うための設定ファイルを用意します。

<redpen-conf lang="ja">
  <validators>
    <validator name="JavaScript" />
  </validators>
</redpen-conf>

あとは、設定ファイルとサンプルテキストを指定して RedPen を実行します。

% ./redpen-distribution-1.3/bin/redpen -c js.xml ranuki.md
[2015-08-29 15:16:02.165][INFO ] cc.redpen.Main - Configuration file: /home/kenhys/work/java/redpen/js.xml
[2015-08-29 15:16:02.169][INFO ] cc.redpen.ConfigurationLoader - Loading config from specified config file: "/home/kenhys/work/java/redpen/js.xml"
[2015-08-29 15:16:02.176][INFO ] cc.redpen.ConfigurationLoader - Succeeded to load configuration file
[2015-08-29 15:16:02.176][INFO ] cc.redpen.ConfigurationLoader - Language is set to "ja"
[2015-08-29 15:16:02.176][WARN ] cc.redpen.ConfigurationLoader - No type configuration...
[2015-08-29 15:16:02.177][INFO ] cc.redpen.ConfigurationLoader - No "symbols" block found in the configuration
[2015-08-29 15:16:02.222][INFO ] cc.redpen.config.SymbolTable - "ja" is specified.
[2015-08-29 15:16:02.222][INFO ] cc.redpen.config.SymbolTable - "normal" type is specified
[2015-08-29 15:16:02.720][INFO ] cc.redpen.parser.SentenceExtractor - "[。, ?, !]" are added as a end of sentence characters
[2015-08-29 15:16:02.720][INFO ] cc.redpen.parser.SentenceExtractor - "[’, ”]" are added as a right quotation characters
[2015-08-29 15:16:02.730][INFO ] cc.redpen.validator.JavaScriptValidator - JavaScript validators directory: /home/kenhys/work/java/redpen/js
ranuki.md:3: ValidationError[JavaScript], [ra-removed-word.js] your sentence has validation error : ら抜き言葉を使用しています。 at line: お刺身を食べれない。

期待通りに、食べ「れ」ないという「ら抜き言葉」を検出することができました。

まとめ

RedPen の Validator を JavaScript で書く方法を紹介しました。 RedPen 1.3 からは JavaScript でバリデーションを簡単に書くことができます。 これまでなら Validator を Java で実装するしかなかったので、大分敷居が下がったのではないでしょうか。

追記

その後、開発者の方から記事に関するフィードバックをもらいました。 「次のバージョン(1.4?)ではvar message=、みたいな定数を定義せずゴリっとエラーメッセージをハードコードできるようになりました。お試しくださいませ!」とのことです。

var message の定義が不要になり、エラーメッセージには addError を呼ぶスタイルになるようです。

  1. RedPen では TokenElement として表現される。