[[構文解析の記事一覧]] *目次 [#yca86807] #contents *jparsec [#e9851712] http://jparsec.codehaus.org/ パーサ生成フレームワーク YACCとの違いは外部ファイルを必要としない点が違う。 Ruby版も存在しており、rparsecという。言語の先頭1文字をとって区別をつけている。 haskell版もあるがこちらが、元になっているので、こちらの名前はparsecという。 *チュートリアル [#sf63f7e4] http://jparsec.codehaus.org/jparsec2+Tutorial **日本語のjparsec使用ブログ [#x951ebb7] だいたいチュートリアルの和訳相当だと思っていいです。 http://d.hatena.ne.jp/taichitaichi/20071008/1191808121 *俺的チュートリアル [#la10fd7b] 実は、jparsecのソースコードをダウンロードすると、計算機のサンプルコードが入っていて、 これがチュートリアルで解説してあるようなコードよりもすっきりさわやかなコードなのだ。 だから、このサンプルから逆に構築する手順を、観察力+妄想力で、作り、俺的チュートリアルをつくるのだ!。それが、漢ってもんだろ。 **ゴール [#k0421c8f] はっきりとしたゴールがあるってことは、それだけでも、しあわせなことなのさ。 /** * The main calculator parser. * * @author Ben Yu */ public final class Calculator { /** Parsers {@code source} and evaluates to an {@link Integer}. */ public static int evaluate(String source) { return parser().parse(source); } static final Parser<Integer> NUMBER = Scanners.INTEGER.map(new Map<String, Integer>() { public Integer map(String text) { return Integer.valueOf(text); } }); static final Binary<Integer> PLUS = new Binary<Integer>() { public Integer map(Integer a, Integer b) { return a + b; } }; static final Binary<Integer> MINUS = new Binary<Integer>() { public Integer map(Integer a, Integer b) { return a - b; } }; static final Binary<Integer> MUL = new Binary<Integer>() { public Integer map(Integer a, Integer b) { return a * b; } }; static final Binary<Integer> DIV = new Binary<Integer>() { public Integer map(Integer a, Integer b) { return a / b; } }; static final Binary<Integer> MOD = new Binary<Integer>() { public Integer map(Integer a, Integer b) { return a % b; } }; static final Unary<Integer> NEG = new Unary<Integer>() { public Integer map(Integer i) { return -i; } }; private static <T> Parser<T> op(char ch, T value) { return isChar(ch).retn(value); } static Parser<Integer> parser() { Parser.Reference<Integer> ref = Parser.newReference(); Parser<Integer> term = ref.lazy().between(isChar('('), isChar(')')).or(NUMBER); Parser<Integer> parser = new OperatorTable<Integer>() .prefix(op('-', NEG), 100) .infixl(op('+', PLUS), 10) .infixl(op('-', MINUS), 10) .infixl(op('*', MUL), 20) .infixl(op('/', DIV), 20) .infixl(op('%', MOD), 20) .build(term); ref.set(parser); return parser; } } *SQLの解析サンプルについて [#of950202] jparsecをダウンロードし、解凍すると [jparsec-2.0_src]-[examples]-[src]-[org]-[codehaus]-[jparsec]-[examples]-[sql] がある。 **Eclipseに取り込む手順 [#o49a3671] jparsecからダウンロードしてきたファイルを解凍しておきます。 junitのjarファイルも手元になければ、ダウンロードしてきます。 ダウンロードしてきたjunitはjunit-4.18.jarとかバージョン名がついているので、 junit.jarという名前にかえておきます。 junitはjparsecのlibフォルダに格納しておきます。 では、eclipseがわの準備を行ってみましょう。 Eclipseに新規にJavaプロジェクトを作成します。 ファイルメニューのインポートで先ほど解凍してできたフォルダを選択し、それをプロジェクトのsrcディレクトリを指定してとりこみます。 srcフォルダは、4つあります、本体用、本体test用、example用、exampleテスト用 インポート直後は まだ、プロジェクトのビルドパスにjarが登録されていませんので、コンパイルエラーになっています。 そこで、ビルドパスの設定でparsecのlibフォルダ内のjarをすべて登録します。 コンパイルエラー表示はほぼ消えます。 が、1カ所だけAllTestクラスでエラーになっています。 それは、作者がライブラリをあげたくないからだと build.xmlの80行目に明記してありました。 こんな感じ、 AllTests uses jtc, which is an extra dependency that I don't want to upload. おそらく、Androidのソースを流用したコードだから、著作権の問題であげれないとでも思ったのでしょうか。 それはさておき、 build.xmlには、このクラスのみ除外してコンパイルする記述がありました。 要するにいらないんです。この だからbuild.xmlをいじくりたくなかったので、つぎのようにクラスを書き換えておきました。 package org.codehaus.jparsec; //import org.openqa.jtc.junit.TestSuiteBuilder; import junit.framework.TestSuite; /** * * @author benyu */ public class AllTests extends TestSuite { public static TestSuite suite() { //return TestSuiteBuilder.suite(AllTests.class); return null; } } **コンパイル方法 [#v14b1801] build.xmlがあるので、toolは、ソースが公開されていないみたいなので、開発元の方用のantタスクかもしれません。それ以外はコンパイルできました。 **exampleにあるSQLパーサの使い方 [#ded7b6e6] exampleのテストケースをみると使い方が書いてありました。 ***どこに書いてあるかというと [#fae6fde4] ***パッケージ名: [#n1f6c4af] package org.codehaus.jparsec.examples.sql.parser; ***クラス名: [#i643a3ac] RelationParserTest ***メソッド名: [#s819af7a] public void testSelect() { ***内容の抜粋: [#p5e84e0f] SQLの問い合わせ文 select distinct 1, 2 as id from t1, t2 が下記のようにクラスの構造に解析されているのを確認しているテストコードが書かれていました。 Parser<Relation> parser = RelationParser.select(NUMBER, NUMBER, TABLE); assertParser(parser, "select distinct 1, 2 as id from t1, t2", new Select(true, Arrays.asList(new Projection(number(1), null), new Projection(number(2), "id")), Arrays.asList(table("t1"), table("t2")), null, null, null)); *persecのよみもの [#g2d334d4] **Parsec, 高速なコンビネータパーサ [#rf72fbda] 文字コードをEUCにしないと文字化けします。 http://www.lab2.kuis.kyoto-u.ac.jp/~hanatani/tmp/Parsec.html