21

私はScalaにかなり慣れていないので、パーサーコンビネーター(パーサーコンビネーターの背後にある魔法、Scalaのドメイン固有言語)について読んでいるときに、ようなメソッド定義に出くわしました。

def classPrefix = "class" ~ ID ~ "(" ~ formals ~ ")"

(tilde)という名前のメソッドを定義するscala.util.parsing.ParsersのAPIドキュメントを読んでいますが、上記の例での使用法をまだよく理解していません。その例では、(チルダ)はjava.lang.Stringで呼び出されるメソッドであり、そのメソッドがなく、コンパイラーが失敗します。(チルダ)は次のように定義されていることを私は知っています

case class ~ [+a, +b] (_1: a, _2: b)

しかし、これは上記の例でどのように役立ちますか?

誰かがここで何が起こっているのかを理解するためのヒントをくれたら嬉しいです。事前にどうもありがとうございました!

1月

4

3 に答える 3

30

ここの構造は少し注意が必要です。まず、これらのことを常にパーサーのサブクラスclass MyParser extends RegexParsersで定義していることに注意してください。さて、あなたは中に2つの暗黙の定義に気付くかもしれませんRegexParsers

implicit def literal (s: String): Parser[String]
implicit def regex (r: Regex): Parser[String]

これらが行うことは、任意の文字列または正規表現を取得し、それらをその文字列またはその正規表現をトークンとして一致するパーサーに変換することです。それらは暗黙的であるため、必要なときにいつでも適用されます(たとえば、そのメソッドを呼び出す場合Parser[String]StringまたはRegex)にはありません)。

しかし、これは何Parserですか?これは、内部で定義された内部クラスであり、次ParsersのスーパートレイトですRegexParser

class Parser [+T] extends (Input) ⇒ ParseResult[T]

入力を受け取って結果にマッピングする関数のようです。まあ、それは理にかなっています!そして、ここでそのドキュメントを見ることができます。

~これで、メソッドを調べることができます。

def ~ [U] (q: ⇒ Parser[U]): Parser[~[T, U]]
  A parser combinator for sequential composition
  p ~ q' succeeds if p' succeeds and q' succeeds on the input left over by p'.

だから、私たちが次のようなものを見たら

def seaFacts = "fish" ~ "swim"

まず、メソッド"fish"がない~ため、暗黙的にメソッドParser[String]があります。次に~、メソッドは型の引数を必要Parser[U]とするため、暗黙的に(つまり== )に変換"swim"します。これで、入力に一致するものができました。入力に残っているものはすべて一致する必要があります。両方の場合は、一致に成功します。Parser[String]UString"fish""swim"seaFacts

于 2011-07-25T16:03:12.850 に答える
13

パーサーの~メソッドは、2つのパーサーを1つに結合し、2つの元のパーサーを連続して適用し、2つの結果を返します。それは単純に(でParser[T]

def ~[U](q: =>Parser[U]): Parser[(T,U)]. 

2つ以上のパーサーを組み合わせたことがない場合は、問題ありません。ただし、それらの3つをチェーンするp1p2、、、、、リターンp3タイプ、、、、、、、つまりタイプは。になりT1ます。そして、あなたの例のようにそれらの5つを組み合わせると、それはになります。次に、結果のパターンマッチを行うと、これらすべての括弧も作成されます。T2T3p1 ~ p2 ~ p3p1.~(p2).~(p3)Parser[((T1, T2), T3)]Parser[((((T1, T2), T3), T4), T5)]

case ((((_, id), _), formals), _) => ...

これは非常に不快です。

次に、巧妙な構文上のトリックがあります。ケースクラスに2つのパラメーターがある場合、パターンのプレフィックス位置ではなく、インフィックスで表示される可能性があります。つまり、があれば case class X(a: A, b: B)、とのパターンマッチングが可能ですが、。とのパターンマッチングcase X(a, b)も可能case a X bです。(これは、x::xs空でないリストに一致するパターンで行われることで::あり、ケースクラスです)。ケースを書くときa ~ b ~ c、それはを意味しますcase ~(~(a,b), c)が、それよりもはるかに快適で、快適であり、case ((a,b), c)正しく理解するのは難しいです。

したがって、パーサーのメソッドは、の代わりにを~返すので、複数の〜の結果でパターンマッチングを簡単に行うことができます。それに加えて、 そしてあなたが得ることができるのと同じくらい同型である、そしてほとんど同じことです。Parser[~[T,U]]Parser[(T,U)]~[T,U](T,U)

結果のコードは読みやすいため、パーサーの結合メソッドと結果タイプには同じ名前が選択されます。結果処理の各部分が文法規則の項目にどのように関連しているかがすぐにわかります。

parser1 ~ parser2 ~ parser3 ^^ {case part1 ~ part2 ~ part3 => ...}

Tildaが選択されたのは、その優先順位(緊密にバインドされている)がパーサー上の他の演算子とうまく機能するためです。

最後にもう1つ、補助演算子が~>あり<~、オペランドの1つの結果、通常は有用なデータを持たないルールの定数部分を破棄します。だから、むしろ書く

"class" ~> ID <~ ")" ~ formals <~ ")"

結果のIDとフォーマルの値のみを取得します。

于 2011-07-25T16:27:34.240 に答える
3

Parsers.Parserをチェックアウトする必要があります。Scalaは、パターンマッチングなどを支援するために、同じ名前のメソッドとケースクラスを定義することがあります。これは、Scaladocを読んでいる場合は少し混乱します。

特に、"class" ~ IDと同じ"class".~(ID)です。~パーサーを別のパーサーと順番に組み合わせる方法です。

値からパーサーを自動的に作成する暗黙の変換が定義されています。したがって、自動的にのインスタンスになります。RegexParsersString"class"Parser[String]

val ID = """[a-zA-Z]([a-zA-Z0-9]|_[a-zA-Z0-9])*"""r

RegexParsersまた、値からパーサーを自動的に作成する別の暗黙的な変換も定義しRegexます。したがって、ID自動的にのインスタンスにParser[String]もなります。

2つのパーサーを組み合わせることにより、リテラル「クラス」に一致する"class" ~ IDaを返し、次に正規表現が順番に表示されます。とのような他の方法があります。詳細については、Scalaでのプログラミングをご覧ください。Parser[String]ID||||

于 2011-07-25T16:01:35.687 に答える