10

以下のコマンドの文法を定義しようとしています。

object ParserWorkshop {
    def main(args: Array[String]) = {
        ChoiceParser("todo link todo to database")
        ChoiceParser("todo link todo to database deadline: next tuesday context: app.model")
    }
}

2 番目のコマンドは、次のようにトークン化する必要があります。

action = todo
message = link todo to database
properties = [deadline: next tuesday, context: app.model]

以下に定義する文法でこの入力を実行すると、次のエラー メッセージが表示されます。

[1.27] parsed: Command(todo,link todo to database,List())
[1.36] failure: string matching regex `\z' expected but `:' found

todo link todo to database deadline: next tuesday context: app.model
                                   ^

私が見る限り、メッセージの単語を照合するパターンは、プロパティのキーと値のペアのキーのパターンとほぼ同じであるため、パーサーはメッセージがどこで終了し、プロパティが開始するかを判断できないため、失敗します。次のように、各プロパティに開始トークンを使用することを主張することで、これを解決できます。

todo link todo to database :deadline: next tuesday :context: app.model

しかし、コマンドをできるだけ自然言語に近づけたいと思います。2 つの質問があります。

エラーメッセージは実際にはどういう意味ですか? また、指定された入力文字列で機能するように既存の文法を変更するにはどうすればよいでしょうか?

import scala.util.parsing.combinator._

case class Command(action: String, message: String, properties: List[Property])
case class Property(name: String, value: String)

object ChoiceParser extends JavaTokenParsers {
    def apply(input: String) = println(parseAll(command, input))

    def command = action~message~properties ^^ {case a~m~p => new Command(a, m, p)}

    def action = ident

    def message = """[\w\d\s\.]+""".r

    def properties = rep(property)

    def property = propertyName~":"~propertyValue ^^ {
        case n~":"~v => new Property(n, v)
    }

    def propertyName: Parser[String] = ident

    def propertyValue: Parser[String] = """[\w\d\s\.]+""".r
}
4

1 に答える 1

22

それは本当に簡単です。を使用するときは~、正常に完了した個々のパーサーにバックトラックがないことを理解する必要があります。

したがって、たとえば、messageコロンの前まですべてを取得します。これはすべて許容されるパターンです。次に、of がありproperties、これには が必要ですが、コロン ( によって取り込まれていない最初の文字) しか見つかりません。だから失敗し、失敗します。さて、前述のように は であるため、0回の繰り返しで正常に終了し、正常に終了します。reppropertypropertyNamemessagepropertyNamepropertypropertiesrepcommand

に戻りparseAllます。commandパーサーは、コロンの前のすべてを消費して、正常に戻りました。次に、次の質問をします。入力 ( \z) の最後にいますか? いいえ、すぐ隣にコロンがあるからです。そのため、入力の終わりを期待していましたが、コロンがありました。

コロンの前の最後の識別子を消費しないように、正規表現を変更する必要があります。例えば:

def message = """[\w\d\s\.]+(?![:\w])""".r

ちなみに、使用defすると、式の再評価が強制されます。つまり、これらの各定義は、それぞれが呼び出されるたびにパーサーを作成します。正規表現は、それらが属するパーサーが処理されるたびにインスタンス化されます。すべてを に変更するとval、パフォーマンスが大幅に向上します。

これらはパーサーを定義するものであって、パーサーを実行するわけではないことに注意してください。それはparseAllパーサーを実行するものです。

于 2009-11-25T20:10:31.853 に答える