2011-07-25 5 views
21

私はスカラ座にかなり新しいだと私はこのようなメソッドの定義に出くわしたパーサコンビネータ(The Magic Behind Parser CombinatorsDomain-Specific Languages in Scala)について読んでいる間の理解:Scalaのパーサコンビネータにチルダ

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

私はthrought読んでいますscala.util.parsing.ParsersのAPIドキュメントで、(チルダ)という名前のメソッドを定義していますが、私はまだ上記の例でその使い方を理解していません。 その例(チルダ)はjava.lang.Stringで呼び出され、そのメソッドを持たず、コンパイラが失敗するメソッドです。 私は(チルダ)は

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

として定義されていますが、どのように上記の例では、このヘルプがないことを知っていますか?

誰かが私にここで起こっていることを理解するヒントを与えることができたらうれしいです。 ありがとうございます!

答えて

30

ここの構造はややこしいです。まず、あなたが常にこれらのものを定義していることに注目してください。内部には、いくつかのパーサのサブクラスがあります。 class MyParser extends RegexParsers。さて、あなたはRegexParsers内部の2つの暗黙の定義に注意してください可能性があります。これらは何をするか

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

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

これは何ですか?Parserものは何ですか?それはParsers内で定義された内部クラス、RegexParserためsupertraitだ:それは入力を受け取り、その結果にマッピング機能だよう

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

が見えます。まあ、それは理にかなっています!そして、それについてのドキュメントを見ることができますhere。我々は最初に、されて、何が起こるか

def seaFacts = "fish" ~ "swim" 

のようなものを見た場合、"fish"はそう、~方法を持っていない、だから、

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'. 

今、私たちはただ~方法を調べることができます暗黙のうちにParser[String]に変換されます。 ~メソッドでは、タイプParser[U]の引数が必要なので、暗黙的に"swim"Parser[String](つまりU == String)に変換します。今度は入力"fish"に一致するものがあり、入力に残されているものは"swim"と一致しなければならず、両方の場合はseaFactsが成功します。

+1

ありがとうございます。私はスカラーの "暗黙の変換"機能に慣れていませんでした。 そのトピックに関する素敵な記事がここにあります:http://scalada.blogspot.com/2008/03/implicit-conversions-magical-and.html – Jano

3

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

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

Stringの値からパーサーを自動的に作成するRegexParsersに定義されているan implicit conversionがあります。したがって、"class"は自動的にParser[String]のインスタンスになります。

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

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

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

13

parserの~メソッドは、2つの元のパーサーを連続して適用して2つの結果を返す2つのパーサーを組み合わせています。それは大丈夫だろうあなたが二つ以上のパーサを組み合わせることがない場合は

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

、(Parser[T]に)単にである可能性があります。あなたがp1.~(p2).~(p3)を意味戻り値の型T1T2T3、その後p1 ~ p2 ~ p3、とそれらの3、p1p2p3を、チェーン場合は、タイプParser[((T1, T2), T3)]です。あなたの例のように5つを組み合わせると、それはParser[((((T1, T2), T3), T4), T5)]になります。結果にパターンマッチングを行うと、すべてのパランシェも同じようになります。

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

これは非常に不快です。

次に、巧妙な構文トリックが来ます。 caseクラスに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の~メソッドは、Parser[(T,U)]の代わりにParser[~[T,U]]を返します。したがって、複数の〜の結果に簡単にパターンを一致させることができます。それ以外にも、~[T,U](T,U)は、あなたが得ることができる同形的なものとほぼ同じものです。

結果のコードが自然に読み込まれるため、パーサーと結果の型の結合方法に同じ名前が選択されます。結果処理の各部分が文法規則の項目にどのように関連しているかがすぐにわかります。その優先順位は(それがしっかりと結合する)パーサ上の他の事業者とうまく演じているので

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

ティルダが選択されています。

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

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

結果にはIDと正式な値しか得られません。

+0

申し訳ありませんが、私はStringに関する質問の一部を見逃しました。 @レックスカーはそれに適切に答えました。だから〜は、Parserのメソッドと、それが返すデータの型の両方です。また、メソッドとしてStringに適用すると、暗黙的に文字列をパーサに変換します。 –

関連する問題