1

レクサーとして機能するパーサーを作成しました。このレクサーはファイルを解析し、トークンのリストを返します。各トークンは、共通の特性を拡張するケースクラスまたはオブジェクトです。

現在、レクサーの出力用のパーサーを作成しようとしていますが、非常に紛らわしい問題が発生しました。パーサーは、ケースオブジェクトを暗黙的にキャストできますが、apply(classHere)手動で呼び出そうとすると、フィットをスローします。

以下は私のコードの簡略版です。

// CODE
trait Token

case class StringWrapperIgnoresCase(val string: String) {
  private case class InnerWrapper(s: String)

  lazy val lower = string.toLowerCase

  override lazy val hashCode = InnerWrapper(lower).hashCode

  override def equals(that: Any) =
    that.isInstanceOf[StringWrapperIgnoresCase] &&
      lower == that.asInstanceOf[StringWrapperIgnoresCase].lower
}

case class ID(val text: String)
  extends StringWrapperIgnoresCase(text)
  with Token {
    override def toString = "ID(" + text + ")"
  }

case object PERIOD extends Token

object Parser extends Parsers {
  type Elem = Token

  def doesWork: Parser[Token] = PERIOD

  def doesNotWork: Parser[Token] = ID
}

コンパイラは、次のメッセージを報告しますdoesNotWork

// ERROR MESSAGE
type mismatch;  found   : alan.parser.ID.type (with underlying type object alan.parser.ID)  required: alan.parser.Parser.Parser[alan.parser.Token]

どうすればこれを修正できますか?

4

2 に答える 2

2

更新:あなたの質問からあなたが何を求めているのか正確にはわかりませんでしたが、あなたIDはあなたの答えのいずれかに一致するパーサーが欲しいと指定したので、ここにもっと慣用的な解決策があります:

val id: Parser[ID] = accept("ID", { case i: ID => i })

ここでは、パーサーが必要とするもの(エラーメッセージ用)の説明と、IDドメインとしてsを使用する部分関数を提供しました。acceptIfxiefeiが回答へのコメントで提供するバージョンを使用することもできます。


パラメータリストなしで(caseオブジェクトではなく)caseクラスを参照すると、クラス自体のインスタンスではない、自動的に生成されたコンパニオンオブジェクトが取得されます。次のことを考慮してください。

sealed trait Foo
case class Bar(i: Int) extends Foo
case object Baz extends Foo

Baz: Fooは問題ありBar: Fooませんが、表示されているものと非常によく似たエラーが発生します。

ここで起こっていることは厳密にキャストしているわけではないことにも注意してください。トレイトには、次のParsersシグネチャを持つメソッドがあります。

implicit def accept(e: Elem): Parser[Elem]

あなたがこれを書くとき:

def doesWork: Parser[Token] = PERIOD

Elemをとして入力しようとするParser[Elem]と、暗黙の変換が開始されます(暗黙の変換の詳細については、仕様のセクション7.3を参照してください)。一方、これを書くとき:

def doesNotWork: Parser[Token] = ID

IDコンパニオンオブジェクト(タイプID.type、not ID、またはToken、したがってnot Elem)をとして入力しようとしていますParser[Elem]が、これを可能にする暗黙の変換はありません。

おそらく、少なくとも今のところは、コードをコンパイルしようとするときに、次のような非推奨の警告に従って、accept(PERIOD)書き出す方がよいでしょう。accept(ID("whatever"))

ケースツーケースの継承には、修正される可能性が低い潜在的に危険なバグがあります。

于 2012-11-29T04:13:55.600 に答える
0

TravisBrownとdrstevensが言ったことを使用して、パーサーに新しいプロダクションを追加しました。

def id = {
  acceptIf(
    _ match {
      case ID(_) => true
      case _ => false
    }
  )("'ID(_)' expected but " + _ + " found")
}

def nowWorks = id

誰かがこれよりもエレガントなソリューションを提供できるようにするために、当面はこれを答えとして受け入れません。これは私の好みには少し厄介に見えます。関数型プログラミングのアプローチに慣れている人なら、これをエレガントなワンライナーに変えることができると確信しています。

于 2012-11-29T04:42:51.960 に答える