最初の例では、中括弧の照合を行います。コンソールは、一連の左中括弧、続いて同数の閉じ中括弧、最後に 0 個以上の行終了文字 (キャリッジ リターン)、そして最後にファイルの終わりを入力します。シンボル ( Ctrl+D)。
有効な文字列の例:
"{}"、"{
{
{
{
{}}}}" ...
不正な文字列の例:
「{ { { {」、「{}{}」、「{}}」、「{ { }{}}」...
最初の例のコードは次のとおりです。
PARSER_BEGIN(Simple)
package com.github.gambo.javacc.simple;
/** Simple brace matcher. */
public class Simple {
/** Main entry point. */
public static void main(String args[]) throws ParseException {
Simple parser = new Simple(System.in);
parser.Input();
}
}
PARSER_END(Simple)
/** Root production. */
void Input() :
{}
{
MatchedBraces() ("\n"|"\r")* <EOF>
}
/** Brace matching production. */
void MatchedBraces() :
{}
{
"{" [ MatchedBraces() ] "}"
}
終端記号と非終端記号
JavaCC 入力構文のトークン (正規表現) は、単純な文字列またはより複雑な正規表現のいずれかです。ibput メソッドに、ファイルの終わりの文字に一致する正規表現 "<EOF>" があることがわかります。すべての複雑な正規表現は山かっこで囲まれます。このように特定の記号を表す記号的な正規表現を「終端記号」と呼び、("\n"|"\r") がこれに分類されます。
上の例の 2 つのメソッド、Input および MatchedBraces は、非終端記号と呼ぶことができます。これは、一連の終端記号および/または他の非終端記号によって形成される文法として定義できます。
各非終端記号は、最後に中括弧で定義されます。通常、この中括弧には一連の宣言とステートメントが含まれており、これらは一般的な宣言とステートメントとして生成されたメソッドに生成されます。上の例では宣言がないため、中括弧は空のように表示されます。この後に中括弧で囲まれた展開が続きます。
非終端の「Input」は、非終端の「matchedbrace」に展開され、その後に 0 個以上の行末文字 (「\n」または「\r」)、そしてファイルの終端文字が続きます。
非終端の「matchedbrace」は展開されてトークン「{」で始まり、その後にオプションのネストされた「matchedbrace」が続き、最後にトークン「}」で終わります。角括弧 [xxx] は、xxx が JavaCC 入力ファイル内でオプションであることを示します。(xxx)? と書くこともできます。これら 2 つの形式は同等です。非終端記号の要素は、次の構造として定義することもできます。
e1 | e2 | e3 | ... : e1、e2、e3 などから 1 つを選択
(e)+ : e が 1 回以上出現
(e)* : e が 0 回以上出現
これらの構造体を埋め込むことも可能
(( e1 | e2 )* [ e3 ] ) | e4のような構造を取得できます。
パーサーを構築する
このパーサーを構築するには、このファイルに対して JavaCC コマンドを実行し、結果の Java ファイルをコンパイルします:
javacc Simple.jj
javac *.java
Ant 環境をセットアップしたので、前のセクションの方法に従ってパーサーを構築できます。ビルド操作と生成されるコードは次のとおりです。
次に、生成されたパーサーを実行して結果を確認します。
正しい実行結果は次のとおりです。
{
{
{}}}
^D
Disconnected from the target VM, address: '127.0.0.1:54377', transport: 'socket'
エラー インスタンスの実行結果は次のとおりです。
{
{
{}}}}}}{}
Exception in thread "main" com.github.gambo.javacc.simple.ParseException: Encountered " "}" "} "" at line 1, column 7.
Was expecting one of:
<EOF>
"\n" ...
"\r" ...
at com.github.gambo.javacc.simple.Simple.generateParseException(Simple.java:249)
at com.github.gambo.javacc.simple.Simple.jj_consume_token(Simple.java:187)
at com.github.gambo.javacc.simple.Simple.Input(Simple.java:44)
at com.github.gambo.javacc.simple.Simple.main(Simple.java:11)
次に、中括弧の間に散在するスペース文字をサポートするように上記のパーサーを拡張します。
"{
{ }\n}\n\n" これは正当です。コードは以下のように表示されます。
字句仕様
PARSER_BEGIN(Simple2)
package com.github.gambo.javacc.simple;
/** Simple brace matcher. */
public class Simple2 {
/** Main entry point. */
public static void main(String args[]) throws ParseException {
Simple2 parser = new Simple2(System.in);
parser.Input();
}
}
PARSER_END(Simple2)
SKIP :
{
" "
| "\t"
| "\n"
| "\r"
}
/** Root production. */
void Input() :
{}
{
MatchedBraces() <EOF>
}
/** Brace matching production. */
void MatchedBraces() :
{}
{
"{" [ MatchedBraces() ] "}"
}
前の例よりも「SKIP」で始まるフィールドが 1 つ増えています。この領域には、スペース、タブ、改行、リターンの 4 つの正規表現があります。これは、これらの正規表現の一致する部分が無視されることを意味します。したがって、これら 4 つの文字のいずれかに遭遇すると、それらは破棄されます。
SKIP に加えて、JavaCC には他に 3 つの字句仕様があります。
- TOKEN : これは、字句トークンを指定するために使用されます。
- SPECIAL_TOKEN : これは、解析中に無視される語彙トークンを指定するために使用されます。この観点から見ると、SPECIAL_TOKEN は SKIP と同じです。ただし、これらのトークンは、適切な処理のためにパーサー操作中に復元できます。
- MORE : 部分トークンを指定します。完全なトークンは、一連の MORE とそれに続くトークンまたは SPECIAL_TOKEN で構成されます。
SPECIAL_TOKEN などについては後の章で紹介します。
上記の例をさらに拡張して、一致する中括弧の数を数えてみましょう。
PARSER_BEGIN(Simple)
package com.github.gambo.javacc.simple;
/** Simple brace matcher. */
public class Simple {
/** Main entry point. */
public static void main(String args[]) throws ParseException {
Simple parser = new Simple3(System.in);
parser.Input();
}
}
PARSER_END(Simple)
SKIP :
{
" "
| "\t"
| "\n"
| "\r"
}
TOKEN :
{
<LBRACE: "{">
| <RBRACE: "}">
}
/** Root production. */
void Input() :
{ int count; }
{
count=MatchedBraces() <EOF>
{ System.out.println("The levels of nesting is " + count); }
}
/** Brace counting production. */
int MatchedBraces() :
{ int nested_count=0; }
{
<LBRACE> [ nested_count=MatchedBraces() ] <RBRACE>
{ return ++nested_count; }
}
ここには、前の例よりも 1 つ多くの TOKEN 定義があります。
JavaCC 字句仕様では、TOKEN
終端記号を定義する部分であり、終端記号の定義の集合から構成されます。各端末定義は 1 つ以上のトークン ユニットで構成されており、トークン ユニットには文字列、正規表現、またはその他の定義方法を使用できます。この例では、TOKEN
2 つの終端記号、つまり左中括弧と右中括弧が定義されていLBRACE
ますRBRACE代表
。山かっこで囲まれています。
このようなタグ仕様は通常、複雑な通常のタグに使用され、単純なトークンの場合は通常変更されません。
この例では、一致する中括弧の数を数える必要があります。宣言領域は、変数「count」と「nested_count」を宣言するために使用されます。非終端の「matchedbrace」がその値を関数の戻り値としてどのように返すかに注目してください。