1

私が観察していたいくつかの非常に紛らわしい動作の根本的な原因を発見しました。ここにテストがあります:

@Test
public void test2() {
    Terminals terminals = Terminals.caseInsensitive(new String[] {}, new String[] { "true", "false" });
    Object result = terminals.tokenizer().parse("d");
    System.out.println("Result: " +  result);
}

これは以下を出力します:

Result: d

terminals.tokenizer()「d」は有効なキーワードまたは演算子ではないため、返されたパーサーが何も返さないことを期待していました。

私が気にする理由は、自分のパーサーの優先度を から返される優先度よりも低くしたかったからですterminals.tokenizer()

public static final Parser<?> INSTANCE =
        Parsers.or(
                STRING_TOKENIZER,
                NUMBER_TOKENIZER,
                WHITESPACE_TOKENIZER,
                (Parser<Token>)TERMINALS.tokenizer(),
                IDENTIFIER_TOKENIZER);

常に一致するため、IDENTIFIER_TOKENIZER上記は使用されません。TERMINALS.tokenizer()

Terminals.tokenizer()未登録のオペレーター/キーワードをトークン化するのはなぜですか? そして、どうすればこれを回避できますか?

4

3 に答える 3

3

次の jParsec 2.2 リリースでは、API によって Terminals の機能がより明確になります: http://jparsec.github.io/jparsec/apidocs/org/codehaus/jparsec/Terminals.Builder.html

「単語」を定義するスキャナーを最初に提供しないと、キーワードを定義することさえできません。

実装は、最初に提供された単語スキャナーを使用してすべての単語を検索し、次にスキャンされた単語の特別なキーワードを識別します。

では、なぜこのようにするのでしょうか。

  1. 大文字と小文字を区別する必要がない場合は、キーワードを「演算子」として渡すことができます。はい、あなたはそれを正しく読みました。それらのトークンレベルのパーサーを同様に使用Terminals.token(op)または取得できます。Terminals.token(keyword)演算子とキーワードを区別するのは、キーワードが「特別な」単語であるということだけです。それらがたまたまアルファベット文字であるか他の印刷可能な文字であるかは、慣習によるものです。
  2. それを行う別の方法は、単語スキャナーを正確に定義することParsers.or(Scanners.string("keyword1"), Scanners.string("keyword2"), ...)です。その後、ターミナルは他のものをトークン化しようとしません。
  3. 上記は、2 フェーズの解析を行うことを前提としています。しかし、それはオプションです。あなたのテストは、を使用してトークナイザーをトークンレベルのパーサーにフィードしていないことを示していますParser.from(tokenizer, delim)。2 フェーズの解析が必要ない場合は、次のように簡単にできます。 or(stringCaseInsensitive("true"), stringCaseInsensitive("false"))

ポイント 3 の詳細。jParsec では、Haskell の Parsec などの他のパーサー コンビネータでは見られない、2 フェーズの解析により、いくつかの特別な注意事項が作成されます。Haskell では、文字列は文字のリストと同じです。したがって、特別なケーシングによって得られるものは何もありません。many(char 'x')文字列をうまく解析します。

Java では、文字列は文字のリストまたは配列ではありません。同じアプローチを取り、各文字を Character オブジェクトにボックス化して、文字レベルとトークンレベルのパーサーをシームレスに統合できるようにすると、非常に非効率的になります。

これで、文字レベルのパーサーが存在する理由が説明されました。ただし、トークン レベルのパーサーを使用することは完全にオプションです (つまり、TerminalsParser.from()などParser.lexer())。

文字レベルのパーサー (別名スキャナー) のみを使用して、完全に機能するパーサーを作成できます。

例えば:Scanners.string("true").or(Scanners.string("false")).sepEndBy1(delim)

于 2014-12-11T15:59:52.170 に答える