妄想プログラマのらくがき帳 : 10月 2012

2012年10月29日月曜日

AndroidアプリでHello World.

スマホに買い換えてから早5ヶ月が過ぎ去ろうとしています。
なのに、今まで1個もAndroidアプリを作ったことがない!
せっかくのスマホに買い換えたのに、これは勿体ないということで、
Androidアプリを作ってみようと思いったった次第です。
とりあえずHello Worldから始めてみようと思います。

まずアプリを作成するには、最初に開発環境を整える必要があります。
下記の場所からSDKをダウンロードして
Get the Android SDK
そのすぐ下のInstalling the SDKに従って環境をインストールします。
Installing the SDK
Eclipseを使ったことがあれば、インストールはそんなに難しくないと思います。

環境が整ったら早速Hello Worldを表示してみます。
まずはEclipseでプロジェクトを作成します。
メニューの[ファイル] - [新規] - [Android Application Project]をクリックすると、
下記の画面が表示されるので、各項目を入力していきます。



Application Nameはアプリの名前です。ユーザーが目にする名前になります。
Project Nameはプロジェクトディレクトリの名前です。Eclipseに表示される名前でもあります。
Package Nameはアプリのパッケージ名です。

Build SDKはアプリのコンパイルするときに使用するSDKのバージョンです。
Minimun Required SDKはアプリが動作するために最低限必要なSDKのバージョンです。

今回はHello Worldを表示するだけなので、Application Nameに"AndroidSample"を入力し、
その他の項目は自動入力された値とデフォルト値のままとしました。

「次へ」ボタンを押すと、Iconの設定画面が表示されます。
今回はとりあえずそのままにしといて、「次へ」ボタンを押して次へ進みます。

Create Activity画面では、アクティビティクラスを作成するかを設定します。
アクティビティクラスはC#のFormのようなものっぽい?ので、BlankActivityを作成することにしました。

New Blank Activity画面では、アクティビティクラスの詳細を設定します。
...が、僕の環境では何故かNew Blank Activity画面の下側がCreate Activity画面のままとなり、
入力欄が切り替わってくれません(´;ω;`)
とりあえず「完了」ボタンは押せるので、ボタンを押して完了しました。

作成したプロジェクトの[res] - [layout]フォルダには、activity_main.xmlが作成されており、
これがメイン画面になります。画面中央にHello world!が書かれているので、そのままアプリを起動させます。

メニューの[実行] - [実行]の次を実行画面からAndroid アプリケーションを選択し、「OK」ボタンを
押すとアプリが起動します(マシンによっては、かなり時間が掛かります)。

メイン画面が表示され、画面中央に"Hello world!"が表示されました!



Visual Studioと比べるとちょっとメンドイですが、WinApiとかに比べると比較的簡単に
画面を作成することが出来るみたいです。

2012年10月21日日曜日

Irony コメントを文法に追加する。

今回もIronyネタ。

コメントを文法に追加する方法として次の2通りの方法があります。

1.GrammarクラスのNonGrammarTerminals変数に、コメントを表すTerminalクラスを追加する。

GrammarクラスのNonGrammarTerminalsに追加したTerminalは、
文字列をパースする際に無視されるようになります。ParseTreeにも追加されなくなります。
以下が自作GrammarクラスのコンストラクタでコメントをNonGrammarTerminalsに追加する例です。
//コメントを定義
var comment = new CommentTerminal("comment", "//", "\n", "\r");
//コメントを非文法終端記号として追加
this.NonGrammarTerminals.Add(comment);

2.コメントを表すTerminalを文法の1部として扱う

ParseTreeでコメントを扱いたい場合があります。
コメントからドキュメント生成したい!といった場合などです。
そのような場合、1の方法だとParseTreeにコメントが追加されないので不都合です。
こんな時はコメントを文法の中に組み込む方法を使います。
//コメントを定義
var comment = new CommentTerminal("comment", "//", "\n", "\r");
//コメントを文法の一部として追加
ProgramLine.Rule = Statement + ToTerm(";") | comment;
Program.Rule = MakeStarRule(Program, ProgramLine);
こっちの方法だとParseTreeにコメントが追加されます。

2012年10月15日月曜日

Irony ParseTreeに不要な項目を追加しないようにする。

IronyでParser.Parse()を使いParseTreeを構築すると、
1ステートメントの区切りである";"(セミコロン)や、単なる区切りの","(カンマ)といった文字まで、
1つのノードとしてParseTreeに追加されます。

以前のエントリーで定義した文法クラスで作成したParseTree。
画面右側のParseTreeに"; (Key symbol)"というノードが確認できます。














多くの場合、これらのノードはその後の処理において不要なノードであり、
ParseTreeを走査するうえで邪魔なノードになります。
なので、指定した項目をノードに追加しないようにする方法があったら便利ですよね。

そんな「あったらいいなぁ」な方法が、Ironyにはちゃんと用意されています。

方法はとっても簡単です。
文法クラスのコンストラクタでMarkPunctuation()を呼び出して、
ParseTreeに追加しない項目を登録するだけです。

下の例では、";"(セミコロン)をParseTreeに追加しないようにする命令です。
this.MarkPunctuation(";");
この命令をコンストラクタに追加した文法クラスでParseTreeを作成してみると…












画面右側のParseTreeから"; (Key symbol)"というノードが消えているのが分かります。

文法が複雑になればなるほど不要なノードが増えてくるので、MarkPunctuation()をうまく使って、
綺麗なParseTreeが作成されるようにしときましょう。

2012年10月13日土曜日

IronyのGrammarExplorerを使って文法をテストする。

IronyにはGrammarExplorerというツールがあります。
GrammarExplorerを使うことで定義した文法クラスを簡単にテストすることができます。

IronyとGrammarExplorerはこちらからダウンロードできます。
Irony - .NET Language Implementation Kit
Ironyのソリューションファイルの中にGrammarExplorerが含まれています。

GrammarExplorerを起動すると次の画面が表示されます。












画面左上のGrammarコンボボックスの右側にあるボタンでAdd grammarを選択すると、
文法クラスを含むアセンブリを選択するダイアログが表示されます。
初期表示ではDllファイルしか選択できないように見えますが、
ファイル名を直接入力すればexeファイルも選択できます。

アセンブリを選択するとアセンブリに含まれる文法の一覧が表示されるので、
テストしたい文法を選択します。













今回は以前のエントリーで定義したサンプル文法クラスをテストしてみます。

文法を選択すると、終端文字一覧や非終端文字一覧、パーサの状態一覧が各タブページに表示されます。
これらを見れば、ちゃんと意図した通りに定義できているのか確認することができます。

文法のテストで使用するのはTestタブです。
まず、Testタブページのテキストボックスにパース対象の文字列を記述します。
次にParseボタンを押すと、右側のParse Treeにパーシングによって生成されたTreeが表示されます。














Tree上でノードを選択すると、テキストボックスで該当する箇所が選択状態になります。
(ただし、選択状態になるのは該当箇所の先頭文字のみみたいです)
逆の機能もあります。Locate>>ボタンを押すとテキストボックス上のカーソル位置にあるノードが
Tree上で選択状態になります。

パース対象文字列に文法エラーがある場合、ParseTreeは表示されません。
代わりに画面下部のParser Outputにエラーとなった箇所とエラーメッセージが表示されます。
VSのエラー一覧のような感じに表示されます。

また、画面下部のParser TraceでEnable Traceにチェックを入れると、
パーサの動作をトレースするようになります。
チェックをいれてParseボタンを押すと、次のようなトレース情報が表示されます。














パーサの状態、スタックのトップ、入力トークン、パーサの動作が時系列に表示されるので、
パーサが文字列をパースする経過がよく分かります。

GrammarExplorerは複雑な文法を定義するときに欠かせないツールなので、
Ironyを使うなら使いこなせるようになっておきましょう!

2012年10月11日木曜日

C#のパーサジェネレータ Ironyを使ってみた。その3。

前回に引き続き、Ironyについてです。

前回は文法を定義する方法を書きました。今回は定義した文法で実際にパースしてみます。

まずはサンプルソース。
string src =
    "a = 10;" +
    "b = 20;" +
    "c = a + b;" +
    "c *= c;";

MyGrammar grammar = new MyGrammar();
Parser parser = new Parser(grammar);

ParseTree parseTree = parser.Parse(src);
MyGrammarは、前回定義した文法クラスです。
ご覧のとおり、パース処理自体は3行で書けます。

  1. 文法クラスのインスタンスを生成。
  2. 文法インスタンスを引数にパーサクラスのインスタンスを生成。
  3. パーサインスタンスのパーサメソッドに文字列を渡す。

たったこれだけです。
Parser.Parse()の戻り値であるParseTreeはASTになってます。
ParseTree.RootがASTのルートノードで、子ノードがParseTree.Root.ChildNodesに繋がっています。
このASTは様々な情報を持っているので、あんなことやこんなことに使えちゃいます^^

ParseTree.Root変数がnullの場合、パース失敗です。
パースエラーに関する情報は、ParseTreeクラスのParserMessages変数で得ることができ、
エラーとなったソース上の場所はParseTree.ParserMessages[ ].Location、
修正候補のリストはParseTree.ParserMessages[ ].ParserState.ExpectedTerminalsで取得できます。

Ironyを使ってみて感動したのは、凄く簡単に使えるようになる!ということです。
他のパーサジェネレータなどを使うには、文法定義に専用の文法を覚えないとダメな場合が多いですが、
IronyはBNFとC#の文法が分かっていれば、すぐに文法定義ができるようになります。

日本語の資料がほとんど無いのが難点ですが、C#でパーサジェネレータが必要な方は是非使ってみてください!

2012年10月9日火曜日

C#のパーサジェネレータ Ironyを使ってみた。その2。

前回に引き続き、Ironyについてです。

前回はプロジェクトの参照設定にIrony.dllを追加するところまで書きました。
今回はIronyで文法を定義する方法です。

Ironyで文法を定義するには、Irony.Parsing.Grammarを継承した文法クラスを作成し、
そのクラスのコンストラクタに文法を定義する処理を記述します。
下記のコードは試しに作ってみた簡単な文法クラスのコードです。

[Language("MyGrammar")]
public class MyGrammar : Grammar
{
    public MyGrammar() : base(true)
    {
        //
        //終端記号を定義
        //
        
        //数字
        var number = new NumberLiteral("number");
        number.DefaultIntTypes = new [] { TypeCode.Int32,
                                          TypeCode.Int64,
                                          NumberLiteral.TypeCodeBigInt };
        number.DefaultFloatType = TypeCode.Double;
        //識別子
        var identifier = new IdentifierTerminal("identifier");
        //コメント
        var comment = new CommentTerminal("comment", "//", "\n", "\r");

        //
        //非終端記号を定義
        //

        var Expr = new NonTerminal("Expression");
        var Term = new NonTerminal("Term");
        var BinExpr = new NonTerminal("BinaryExpression");
        var BinOp = new NonTerminal("BinaryOperator");
        var AssignmentStmt = new NonTerminal("AssignmentStatement");
        var AssignmentOp = new NonTerminal("AssignmentOperator");
        var Statement = new NonTerminal("Statement");
        var ProgramLine = new NonTerminal("ProgramLine");
        var Program = new NonTerminal("Program");

        //
        //文法を定義
        //

        Expr.Rule = Term | BinExpr;
        Term.Rule = number | identifier;
        BinExpr.Rule = Expr + BinOp + Expr;
        BinOp.Rule = ToTerm("+") | "-" | "*" | "/";
        AssignmentStmt.Rule = identifier + AssignmentOp + Expr;
        AssignmentOp.Rule = ToTerm("=") | "+=" | "-=" | "*=" | "/=";
        Statement.Rule = AssignmentStmt | Expr | Empty;
        ProgramLine.Rule = Statement + ToTerm(";");
        Program.Rule = MakeStarRule(Program, ProgramLine);
        //文法のルートを設定
        this.Root = Program;

        //
        //演算子の優先順位を定義
        //

        RegisterOperators(1, "+", "-");
        RegisterOperators(2, "*", "/");
        
        this.LanguageFlags = LanguageFlags.NewLineBeforeEOF |
                             LanguageFlags.SupportsBigInt;
    }
}
まず、6~9行目で終端記号を定義しています。
数字や識別子、コメントといった一般的な終端記号は、すでにクラスが用意してあるのでそれを使います。
他にもRegexLiteralやCustomTerminalなどといった終端記号用のクラスがあるので、必要に応じて使い分けます。

次に21~33行目で非終端記号を定義しています。
引数で与えた名前は、パースツリー内の各ノード名として使用されます。

35~49行目では文法を定義しています。
'+'と'-'が演算子オーバーロードされているので、BNFライクに記述することができます。
・定義に文字列リテラルを使う場合は、ToTerm()を使う
・'*'(繰り返し)を記述するには、MakeStarRule()を使う
といったところが、Irony独特の記述方法です。

最後に演算子の優先順位の設定とオプションの設定をしています。

以上で簡単な文法ですが定義完了です。
Irony付属のサンプルとかを見ると、C#の文法とかは定義がもっと複雑になるみたいですが、
上記のような簡単な文法なら簡潔に記述することができてイイ感じです^^
(複雑な文法定義に関しては、そのうち別エントリーで書く予定です)

次回は定義した文法でパーサを作成し、実際にパースしてみます。

2012年10月8日月曜日

C#のパーサジェネレータ Ironyを使ってみた。その1。

C#で簡単に使えるパーサジェネレータってないかな~と探していたところ、
Ironyが結構良さげだったので試しに使ってみました。

IronyはオープンソースのLALR(1)パーサジェネレータで、CodePlexで公開されています。
Irony - .NET Language Implementation Kit

Ironyの大きな特徴として、C#のコードで文法を定義できるという点があります。
下記のコードは、Irony - .NET Language Implementation Kitにあるサンプルの一部ですが、
こんな感じでC#で自然に文法を定義できます。

// 2. Non-terminals
var Expr = new NonTerminal("Expr");
var Term = new NonTerminal("Term");
var BinExpr = new NonTerminal("BinExpr", typeof(BinExprNode));
var ParExpr = new NonTerminal("ParExpr");
var UnExpr = new NonTerminal("UnExpr", typeof(UnExprNode));
var UnOp = new NonTerminal("UnOp");
var BinOp = new NonTerminal("BinOp", "operator");
var PostFixExpr = new NonTerminal("PostFixExpr", typeof(UnExprNode));
var PostFixOp = new NonTerminal("PostFixOp");
var AssignmentStmt = new NonTerminal("AssignmentStmt", typeof(AssigmentNode));
var AssignmentOp = new NonTerminal("AssignmentOp", "assignment operator");
var Statement = new NonTerminal("Statement");
var ProgramLine = new NonTerminal("ProgramLine");
var Program = new NonTerminal("Program", typeof(StatementListNode));

// 3. BNF rules
Expr.Rule = Term | UnExpr | BinExpr | PostFixExpr;
Term.Rule = number | ParExpr | identifier;
ParExpr.Rule = "(" + Expr + ")";
UnExpr.Rule = UnOp + Term;

また、ライセンスはMITライセンスなので、著作権・無保証の明示をしておけば利用や改変は自由です。
ちなみにIrony - .NET Language Implementation KitのSystem Requirementsに
>>Windows 7, .NET Framework 4.0, Visual Studio 2010
とありますが、VistaやVSExpress2010でも使えます(XPは確認していませんが、たぶん使えます)。

さてIronyの使い方ですが、まずはCodePlexのページのDOWNLOADSからzipファイルを落としてきます。
zipファイルの中にはIronyのソリューション一式が入ってるので、ソリューションを開きます。

※このときVSがExpressの場合、テスト関係のプロジェクトなど一部のプロジェクトが開けませんが、
 Ironyを使うだけなら問題ないです。ソリューションを開くごとにメッセージが表示されるので、
 うっとおしい場合は(利用不可)となっているプロジェクトをソリューションから削除しとくと楽です。

ソリューションが開けたら、ビルドを行いIrony.Dllを作成します(Irony\bin\Debug[or Release]に出力されます)。
この出来上がったDllを、パーサを使いたい(作りたい)プロジェクトの参照に追加したら準備完了です。

今回はここまで。次回は文法の定義の仕方について書きます。