2009年01月28日
10分で書ける、お手軽パーサーを AS3 で
10分で書ける、お手軽パーザー にて C++ での数式パーサーが50行ほどで実装されていたので、これを AS3 で書き直してみた。
これが完成品。
数式を書き換えたらその場で計算してくれる。簡易 eval みたいな感じ。
再帰下降法と聞いてもピンと来なかったんだけど、移植する過程で理解が深まっていった。本家の説明が大変分かりやすいので、詳しい仕組みについては 10分で書ける、お手軽パーザー のほうをごらんあれ。
あ、オリジナルでは -3 などのマイナスのつく数字が扱えなかったので、自分で修正してみた。
以下、AS3 版のソースコード(97行)。
// Simple Recursive Descent Parsing // see also: http://fxp.hp.infoseek.co.jp/arti/parser.html package{ import flash.display.*; import flash.events.Event; import flash.text.*; [SWF(backgroundColor="#ffffff")] public class SimpleRecursiveDescentParsing extends Sprite{ public function SimpleRecursiveDescentParsing(){ var parser:Parser = new Parser(); var output:TextField = new TextField(); output.x = 10; output.y = 40; output.autoSize = "left"; addChild(output); var input:TextField = new TextField(); input.border = true; input.x = 10; input.y = 10; input.width = 200; input.height = 20; input.type = "input"; input.text = "5 * (1 + 2)"; input.addEventListener("change", function(event:*):void{ try{ output.text = parser.parse(input.text); output.textColor = 0x000000; }catch(e:Error){ output.text = e.toString(); output.textColor = 0xff0000; } }); input.dispatchEvent(new Event("change")); addChild(input); scaleX = scaleY = 2; } } } class Parser{ private var pos:int; private var str:String; public function parse(s:String):String{ str = s.replace(/ /g, ""); pos = 0; return expr().toString(); } // Expr = Term { (+|-) Term} private function expr():int{ var ret:int = term(); while(true){ switch(str.charAt(pos)){ case "+": pos++; ret += term(); break; case "-": pos++; ret -= term(); break; default: return ret; } } return 0; // never comes here } // Term = Fact { (*|/) Fact} private function term():int{ var ret:int = fact(); while(true){ switch(str.charAt(pos)){ case "*": pos++; ret *= fact(); break; case "/": pos++; ret /= fact(); break; default: return ret; } } return 0; // never comes here } // Fact = ( Expr ) | - Fact | number private function fact():int{ var ret:int; var m:Array; if((m = str.substr(pos).match(/^(\d+)/))){ pos += m[1].length; return parseInt(m[1]); } else if(str.charAt(pos) == "-"){ pos++; return -fact(); } else if(str.charAt(pos) == "("){ pos++; ret = expr(); if(str.charAt(pos) != ")") throw new Error("No match for )"); pos++; return ret; } throw new Error("invalid format"); } }