Unclazz.Commons.Jsonライブラリv1.2.1リリース
昨年11月に公開したJSONパーサー(かつシリアライザ) Unclazz.Commons.Jsonライブラリ のバージョン1.2.1をリリースしました。
このリリースでは以下の2つのバグフィックスを行っています:
- Stringリテラルに含まれるCRLFなどのエスケープシーケンスを正しく処理できない問題の解消
- Stringリテラルに含まれるUnicodeエスケープシーケンス(サロゲートペア含む)をサポートできていない問題の解消
余談 - サロゲートペア問題
ところで、目的はあくまでバグフィックスだったのですが、とくに2点目のUnicodeエスケープシーケンスのサポート追加はいくつかの点で勉強になりました:
- UTF-16ではUnicodeの拡張領域に含まれる文字を表現するためサロゲートペアという4バイトを使用する(それ以外は2バイト)
- JavaやC#のようなやや旧世代の言語では文字列はUTF-16で表現されている
- これらの言語における文字型(char)はその定義からしてサロゲートペアを扱うにはキャパシティが足りない(Javaでは概念として「文字」と「コードポイント」を分けており後者はサロゲートペアを含む)
- これらの言語における文字列型(String)リテラルで拡張領域に含まれる文字をUnicodeエスケープシーケンスで表現する場合
\uXXXX\uXXXX
(XXXXは左ゼロ詰め4桁固定16進数値)という記法をとる - これは「あたかも2文字であるように記述する」とも読めるが実際上その長さ(Javaでは
String#length
、C#ではString#Length
)をとると2文字分に該当する - 「(Python3の)文字列はUnicode文字のシーケンスだ」というとき含意されているのはこうしたUTF-16に依拠する実装をとる前世代に対する優位性である
参考にした記事:
- Unicode メモ - C#練習日記 … サロゲートペアの整数値から文字(というか文字列)に変換する方法を学びました。
- CodeZine - サロゲートペア入門…サロゲートペアの概念について学びました。加えて上記記事とあわせてサロゲートペアの整数値を文字に変換する方法を学びました。
- Dive Into Python 3 日本語版 - 第2章 ネイティブデータ型…上述のPython3に関する記述から今回の問題の示唆を受けました。
- hydroculのメモ > プログラミング言語の比較 > 文字列 > Unicode拡張領域の取り扱い…Java、Python、JavaScriptなどの比較がなされていてわかりやすいです。
2016年11月の記事:
数週間かけてC#でパーサーコンビネーターつくってみた
先日ふとFastParseというScala言語で開発されているパーサーコンビネーター・ライブラリーについての記事を見つけ、それを読んでいるうちにC#言語で似たようなものを作ってみたくなりました。
そして、実際に作ってみたのがこちら。
README.mdからの転載となりますが、例えば次のコードは浮動小数点数のパーサーの実装例です:
sealed class NumberParser : Parser<double> { readonly Parser sign; readonly Parser digits; readonly Parser integral; readonly Parser fractional; readonly Parser exponent; readonly Parser<double> number; public NumberParser() { sign = CharIn("+-").OrNot(); digits = CharsWhileIn("0123456789", min: 0); integral = Char('0') | (CharBetween('1', '9') & digits); fractional = Char('.') & digits; exponent = CharIn("eE") & (sign) & (digits); number = ((sign.OrNot() & integral & fractional.OrNot() & exponent.OrNot()).Capture()).Map(double.Parse); } protected override ParseResult<double> DoParse(Reader input) { return number.Parse(input); } }
こうして作成したパーサーは次のようにして使用します:
var p = new NumberParser(); var r1 = p.Parse("-123.456"); var s1 = r.Successful; // returns true. var c1 = r.Capture; // returns Optional(-123.456), and c1.Value returns -123.456. var r2 = p.Parse("hello"); var s2 = r.Successful; // returns false. var c2 = r.Capture; // throws InvalidOperationException.
FastParseのドキュメンテーションにあるサンプルを真似たものです。
比較してみてもらうと一目瞭然ですが、演算子オーバーロードの許容範囲の違いによる影響は大きいです。内部DSLを作り上げる場合、演算子オーバーロードでどこまでの演算子を(あるいは標準にはない演算子を)サポートできるかは、結果に大きく出てきます。逆に言えばDSLづくりでもしない限りそんなキャパシティはあるだけ無駄ということかもしれませんけど。。細かいところではその他にもいろいろ制約となった事項はあるのですが、くたびれてしまってここに書き出す気力もありません。
つくりながら今更ですが「パーサーコンビネーターにおけるパーサーは値のキャプチャを行わないケースが多いこと」「(パース対象の構文にもよりそうだが)パースの途中経過もしくは最終成果物として抽象構文木のような構造は必要不可欠ではないこと」「パーサーを組み立てる過程で『左再帰』の除去の工夫が必要になること」などなどの事項を学ぶことができました。
『ウィッチ』を観てきました
どういう経緯によるものか忘れましたがFilmarksで登録していた『ウィッチ』を観てきました。舞台は入植時代の北アメリカ。主人公は熱狂的なプロテスタントの家庭に生まれ育った少女。父親は信仰に関わる何らかの理由で入植者コミュニティと衝突。結果家族はコミュニティのプランテーションから追放されて、未だ開拓されていない原野、針葉樹の暗い森の近くに移り住みます。ところがその森には不穏な気配があり・・・。
観終わったところでの感想としては、魔女とそれをめぐる恐怖や狂気を取り扱ったものとしては何だか物足りないなという感じです。雰囲気の演出は悪くないと思います。物足りなく感じたのは「魔女」表象とそれを生み出す同時代人の感性の描き方。「魔女」を自分たちの日常世界からかけ離れたもの、それを侵害するもの、「野蛮」で「残酷」で「狂気」を孕んだものとして規定する、そしてそれに対する「自分たち」=「主人公たち」の恐怖を描き出す、というのはオーソドックスなものではあります。でも、歴史学や社会学(あるいは文化人類学)で中世〜近代の農村社会のあり様の一端を垣間見たうえでこうした作品を見るとなると、やはり足りないと感じるのです。
「魔女」という表象を生み出す「主人公たち」もまた、なるほどそれにふさわしくというべきか、今現在の私たちからするとぎょっとするような「野蛮」で「残酷」で「狂気」に満ちた日常正解を生きていたはずです。それらの「異常」性は本作の主人公たちが元いたコミュニティから排除される要因となった(らしい)熱狂的プロテスタンティズムによってだけ説明されうるものではないでしょう。だから私たちが真の意味で「魔女」と対峙する時、私たちはまず最初に「主人公たち」=中世〜近代の農村社会の住人たちの日常世界に対する強烈な違和感と対峙することになり、次にそれらと本来同種でありながら対抗関係にある「魔女」たち(対抗するものとして「主人公たち」のコミュニティから弾き出された「魔女」たち)への違和感と対峙することになる、そういう順序なのだと思います。
もちろんこの作品を作った人たちの意図というのは、歴史考証を入念に行って社会史的な意味におけるリアリティを追求することではなく、現代以降の日常世界に生きる私たちが自分たちの感性を中世〜近代の農村社会の住人たちにそのまま適用するという一種のアナクロニズムを土台にして想像された「魔女」像を、そのできるだけ「生々しく」描くことなのでしょう。だからそこをあまり指摘しても仕方ないだろうとも思うのですが。。