OreScriptがはやってるならパーサコンビネータの需要もあるのかな?
近頃、JavascriptでOreScriptなんてのがちょっとはやっていたりしまして。
OreScript書くのにある程度ちゃんと動くパーサコンビネータがあれば便利かも、とおもったので以前書いたパーサコンビネータをいじってみました。
変更点
- メソッド名などをHaskellにあわせた
- よくもわるくも、記号含有率をあげた(and -> $に、or -> l に)
- 相互再帰をサポートした
- 左再帰(chainl1)をサポートした
- 右再帰(chanr1)をサポートした
ということで、そこそこの用途に耐えるものになったと思います。
ダウンロード
完全にアンドキュメントです。すみません。ただ、ソースは200行くらいなんで見ればわかるかと。というか、HaskellのParsecのマニュアルを読めば基本一緒なので使い方がわかると思います。
サンプル
id:amachangさんが書いていたような数式パーサを定義してみましょう。
残念ながらLexerはつくっていないので、空白は受け入れられません。
- var ExprParser = Inforno.Parsec.Parsers.define(function(){with(this){
- var mul = function(a,b){return a*b;};
- var div = function(a,b){return a/b;};
- var add = function(a,b){return a+b;};
- var sub = function(a,b){return a-b;};
- this.numbers = many1( chrLike(function(c) { return (c>= '0' && c <= '9'); }) ) .ret(function(ns){
- return parseInt(ns.join(""));
- });
- this.expr = chainl1(p("term"), p("addop"));
- this.term = chainl1(p("factor"), p("mulop"));
- this.factor = (between(chr("("), p("expr"), chr(")"))) .l (numbers)
- this.mulop = (chr("*") .retval(mul)) .l (chr("/") .retval(div));
- this.addop = (chr("+") .retval(add)) .l (chr("-") .retval(sub));
- }});
- var test = "100*(100+200)/10";
- var parser = new ExprParser(test);
- var parseResult = parser.expr.parse();
- if(parseResult.success()) {
- alert(parseResult.result);
- // => 3000
- }
すごく、Parsecです・・・
こういうのを最近はやりのCodeReposでやればいいのかなあ。
Javascriptでパーサジェネレータを書いてみた
ちょっと前にjavascriptで構文解析とかがはやった気がするので、javascriptのリハビリがてらかいてみた。
ググってみると
あたりがあるのだが、まぁ勉強ということで。javascriptらしく書いてみようかと。
ということで、モナドがどーたらとか難しい話はまぁおいておいて、簡単に値がとりだせますよ、という見栄え重視で作ってみた。基本的な機能しかない。けど拡張するのは簡単。せめて相互再帰くらいは実装したほうがよかったかな。まぁ、こんなの真剣に使う人もいないと思うので、要望があればってことで。ちなみに依存するライブラリはありません。
ダウンロード:Inforno.Parsec
たとえばこんな感じにCSVのパーサが定義できる。withを使ってDSLっぽくしてみた。
- var CSVParser = Inforno.Parsec.Parsers.define(function(){with(this){
- this.chars = chrLike(function(c){ return c != '' && c!=',' && c!='\n' && c!='"';});
- this.quote = chr('"')
- this.quoteInQuote = str('""') .ret(function(){return '"';});
- this.charInQuote = chrLike(function(c){ return c!='"';});
- this.quotedField = quote .and (rep((quoteInQuote) .or (charInQuote))) .and (quote)
- .ret(function(q1, cs, q2){
- return cs.join("");
- });
- this.field = rep1(chars) .ret(Return.str)
- this.record = sep( (field) .or (quotedField), chr(","), true) .ret(function(fields) {
- return {fields: fields};
- })
- this.csv = sep(record, chr("\n"), true) .and(eof) .ret(function(records) {
- return {records : records};
- })
- }});
- var testCsv = [
- '"aaa","b',
- 'bb","ccc",zzz,"y""Y""y",xxx'
- ].join("\n");
- var parser = new CSVParser(testCsv);
- var parseResult = parser.csv.parse()
- if(parseResult.success()) {
- var records = parseResult.result[0].records;
- for(var i=0,l=records.length;i<l;i++){
- alert("row("+i+") "+records[i].fields);
- }
- }
実はパーサコンビネータって簡単にかけるんだけど、世間的にちゃんと認識されているのかなあ。思うに、パーサコンビネータといえばHaskell界隈であり 、関数型界隈であり(もちろんちゃんとJAVAとかの実装もあるんだけど)、モナドでありそういうところって専門用語が多いから「ふつうの」プログラマからは簡単に見えてないような気がするなあ。
Prototype 1.6.0 RC
でましたね。Prototype 1.6.0 release candidate
WEB界隈から離れてしばらくたちますが、またいつWEB界隈に戻るかわからないので一応追ってます。
1.6ではついにClass周りに手が入りました。Prototype.jsといえばシンプルすぎるClass周りとプロパティをコピーするというなんちゃって継承がウリ(?)だったわけですが、1.6からは違います。もうオレオレクラス定義ライブラリをわざわざ使わなくても大丈夫。
公式にも例がありますが、こんな感じでちゃんと継承できるようになりました。
- var Animal = Class.create({
- initialize: function(name) {
- this.name = name;
- },
- eat: function() {
- return this.say("Yum!");
- },
- say: function(message) {
- return this.name + ": " + message;
- }
- });
- var Eatable = {
- eaten : function() {
- return this.say("help me!!!");
- }
- }
- var Mouse = Class.create(Animal, {
- });
- //syntax sugar for Object.extend
- Class.mixin(Mouse.prototype, Eatable);
- // subclass that augments a method
- var Cat = Class.create(Animal, {
- eat: function($super, food) {
- if (food instanceof Mouse) return [food.eaten(),$super()].join("\n");
- else return this.say("Yuk! I only eat mice.");
- }
- });
- var a = new Cat("Tama");
- alert(a.eat(new Mouse("Nezumy mouse")));
単にプロトタイプチェーンをつなげるだけではなくて、オーバライドされたメソッドの引数名を取得して(toStringしてgrepですね)、$superだった場合、自動的に親クラスのメソッドを$superとして渡してくれます。地味に便利。
あとはsyntax sugarとしてClass.mixinがあります。まぁObject.extendなんですが、こっちの方がわかりやすいですよね。
そのほかもいっぱい機能が追加れさてれてますね。個人的にはdocument.observe("contentloaded", function() { ... })て感じでサポートされたDOM完了時点でのイベントと、
- $("container").observe("titleChanged", function(event) {
- this.highlight({ duration: 0.5 });
- });
- $("title").fire("titleChanged");
って感じのカスタムイベントが必ず使いそうな機能かな。
資格の勉強とかもそろそろしなきゃなー、と思いつつ、Scalaでパーサコンビネータライブラリがついたので、Schemeの処理系でも実装したいなあ、と思い始めてる今日この頃。