解析器を自動生成したい,の巻
2001.04.08 新規
まず、何と言っても Java コンパイラ・コンパイラである
JavaCC
を入手してないと話になりません。
まずは本家のサイト(
Metamata
)から持ってきましょう。
ただし、今回の話は v1.1 という版を対象としてます。
本家からダウンロードできる JavaCC の ドキュメント は、もちろん英語。 それを一部、日本語に翻訳したサイトが JavaCC 文法ファイルの説明 にあるので、これも参考にしましょう。
自動生成されたクラスは、パッチを当てないと日本語を扱えません。
そのパッチを公開してるサイトが
JavaCCで日本語を扱う方法
にあります。
Reader クラスを使っても日本語は普通には使えなかったので、
上記サイトで公開されてる v0.8pre1 用のパッチで修正しましょう。
最初に書いたとおり、ここで対象として使用してるのは v1.1 で、 Java 開発環境のバージョンは v1.3.0_02 を使っています。
さて。ココまで書いておきながらアレですが、もうテンパッてます。
JavaCC
を使用するには BNF 記法に関する知識も必要となりますが、自分自身も勉強中なので説明するのは無茶です。
ということなので、サンプルを用意してみました。
簡単な HTML ファイルを解析して、結果を標準出力するサンプルです。
ソースをダウンロード
コンパイル後の全ファイルをダウンロード
options { STATIC=false; JAVA_UNICODE_ESCAPE=true; UNICODE_INPUT=false; } PARSER_BEGIN(HTMLAnalyzer) import java.util.*; /** * Simple HTML Parser. * * @author Musi_chan * (Musi_chan@cool.biglobe.ne.jp) * @version 2001/04/08 11:06 */ public class HTMLAnalyzer { /****************************************************************************** Fields ******************************************************************************/ private String htmlTitle; private Vector value; private String tab; private String item; /****************************************************************************** Methods ******************************************************************************/ public void init() { value=new Vector(5); tab=""; item=""; } public void setTitle(String title) { htmlTitle=title; } public String getTitle() { return htmlTitle; } public void addValue(String title) { value.addElement(tab+title); } public void addTab() { tab+="\t"; } public void removeTab() { if(!tab.equals("")) { tab=tab.substring(1); } } public void setItemize() { item+=" "; } public void resetItemize() { if(!item.equals("")) { item=item.substring(1); } } public void addItemizeValue(String title) { if(value==null) { value=new Vector(5); } value.addElement(tab+item+"* "+title); } public Vector getValue() { return value; } } PARSER_END(HTMLAnalyzer) /* ************************************************************************* */ /* ****************************** Token field ****************************** */ /** Token(Separators) */ SKIP : { " " | "\t" | "\n" | "\r" | "\f" } /** Token(Comment) */ SPECIAL_TOKEN : { <COMMENT : "<!--" (~[">"])* "-->"> } /** Token(NewLine) */ SPECIAL_TOKEN : { <BR : "<br>" | "<BR>"> } /** Token(Literals) */ TOKEN : { <STRING_LITERAL : (~["<",">","\n","\r","\f"])+> } /** Token(Begin tags) */ TOKEN : { <HTML : "<html>" | "<HTML>"> | <HEAD : "<head>" | "<HEAD>"> | <TITLE : "<title>" | "<TITLE>"> | <BODY : "<body>" | "<BODY>"> | <P : "<p>" | "<P>"> | <UL : "<ul>" | "<UL>"> | <LI : "<li>" | "<LI>"> | <BQ : "<blockquote>" | "<BLOCKQUOTE>"> } /** Token(End tags) */ TOKEN : { <EHTML : "</html>" | "</HTML>"> | <EHEAD : "</head>" | "</HEAD>"> | <ETITLE : "</title>" | "</TITLE>"> | <EBODY : "</body>" | "</BODY>"> | <EP : "</p>" | "</P>"> | <EUL : "</ul>" | "</UL>"> | <EBQ : "</blockquote>" | "</BLOCKQUOTE>"> } /* *************************************************************************** */ /* ****************************** Analyze field ****************************** */ /** Analyze html file */ void analyzeHTML() : { } { html() <EOF> } /** HTML */ void html() : { Token token; } { <HTML> { init(); } <HEAD> <TITLE> token=<STRING_LITERAL> <ETITLE> { setTitle(token.image); } <EHEAD> <BODY> tagList() <EBODY> <EHTML> } /** Tag list */ void tagList() : { } { tag() [tagList()] } /** Tag */ void tag() : { Token token; } { token=<STRING_LITERAL> { addValue(token.image); } | <P> { addValue(""); } (tag())* <EP> | <BQ> { addTab(); } (tag())* <EBQ> { removeTab(); } | ulSection() } /** UL */ void ulSection() : { } { <UL> { setItemize(); } (li() | ulSection())* <EUL> { resetItemize(); } } /** LI */ void li() : { Token token; } { <LI> token=<STRING_LITERAL> { addItemizeValue(token.image); } } |
import java.io.*; import java.util.*; /** * Test program for HTML Parser. * * @author Musi_chan * (Musi_chan@cool.biglobe.ne.jp) * @version 2001/04/08 11:06 */ public class Test { /****************************************************************************** フィールド ******************************************************************************/ /** * 使用するファイル保持用インスタンス変数です. */ private File useFile; /** * ファイル読み出し用のインスタンス変数です. */ private BufferedReader reader; /** * HTMLファイル解析器を保持するインスタンス変数です. */ private HTMLAnalyzer htmlAna; /****************************************************************************** コンストラクタ ******************************************************************************/ /** * コンストラクタです. */ public Test() { } /****************************************************************************** メソッド ******************************************************************************/ /** * メイン・メソッドです. */ public static void main(String[] args) { Test test; if(args.length!=1) { System.exit(1); } test=new Test(); test.setFile("",args[0]); if(test.exists()) { if(test.loadHTMLFile()) { test.printResult(); } } } /** * ファイルを設定するメソッドです. * * @param baseDirectoryName ディレクトリ名 * @param fileName ファイル名 */ public void setFile(String baseDirectoryName,String fileName) { if(baseDirectoryName.equals("")) { useFile=new File(fileName); } else { useFile=new File(baseDirectoryName,fileName); } } /** * ファイルがあるか判定するメソッドです. * * @return ファイルがある場合true */ public boolean exists() { return useFile.exists(); } /** * ロード用のストリームを接続するメソッドです. * * @throws FileNotFoundException ファイルがない場合に発生 */ public void openStreamForLoad() throws FileNotFoundException { // リードストリームを開く reader=new BufferedReader(new FileReader(useFile)); } /** * ロード用のストリームを閉じるメソッドです. */ public void closeStreamForLoad() { if(reader==null) { return; } // リードストリームを閉じる try { reader.close(); } catch(IOException ioe) { } } /** * HTMLの内容を読み込むメソッドです. * * @return 正常終了したらtrue */ public boolean loadHTMLFile() { // ストリームを開く try { openStreamForLoad(); } catch(IOException ioe) { // I/O例外のメッセージ出力 System.out.println("I/O Exception."); return false; } // HTMLパーサを生成 htmlAna=new HTMLAnalyzer(reader); try { // HTMLファイルを解析 htmlAna.analyzeHTML(); } catch(ParseException pe) { // 解析失敗エラーの出力 System.out.println(pe.getMessage()); // ストリームを閉じる closeStreamForLoad(); return false; } // ストリームを閉じる closeStreamForLoad(); return true; } /** * 読み込んだ内容を出力するメソッドです. */ public void printResult() { Vector value; value=htmlAna.getValue(); System.out.println("Title: "+htmlAna.getTitle()); for(int i=0;i<value.size();i++) { System.out.println(""+value.elementAt(i)); } } } |