2012-03-27 10 views
5

の解析:スカラ:私はScalaの構文解析コンビネータライブラリで自分自身を理解を助けるために、おもちゃのHTMLパーサで遊んだマッチングトークン

import scala.util.parsing.combinator._ 
sealed abstract class Node                  
case class TextNode(val contents : String) extends Node          
case class Element(                   
    val tag : String,                   
    val attributes : Map[String,Option[String]],             
    val children : Seq[Node]                  
) extends Node                    

object HTML extends RegexParsers {                
    val node: Parser[Node] = text | element              
    val text: Parser[TextNode] = """[^<]+""".r ^^ TextNode          
    val label: Parser[String] = """(\w[:\w]*)""".r            
    val value : Parser[String] = """("[^"]*"|\w+)""".r         
    val attribute : Parser[(String,Option[String])] = label ~ (         
     "=" ~> value ^^ Some[String] | "" ^^ { case _ => None }       
    ) ^^ { case (k ~ v) => k -> v }               
    val element: Parser[Element] = (               
    ("<" ~> label ~ rep(whiteSpace ~> attribute) <~ ">")          
     ~ rep(node) ~                   
    ("</" ~> label <~ ">")                  
) ^^ {                      
    case (tag ~ attributes ~ children ~ close) => Element(tag, Map(attributes : _*), children) 
    }                       
}                        

私は私が欲しい実現していますどのようなことは、私のことを確認するためにいくつかの方法です。開始タグと終了タグが一致します。私はそれをすると思う

は、私はので、私は終了タグのためのパーサを構築するために開始タグを使用することができます 、flatMapコンビネータ〜Parser[A] => (A => Parser[B]) => Parser[B]のいくつかの並べ替えを必要とします。しかし、私はその署名と一致するものは表示されませんin the library

これを行うには適切な方法はありますか?

答えて

3

あなたは間違った場所を探しています。しかし、それは普通の間違いです。メソッドParser[A] => (A => Parser[B]) => Parser[B]が必要ですが、Parserではなく、Parsersというドキュメントを見ました。

ルックhere

flatMapintoまたは>>とも呼ばれます)があります。

4

intoという名前の同等の方法で、より便利なエイリアス(内包表記のために使用した場合flatMapがまだ必要とされている)であるかもしれないオペレータ>>もあり、パーサのflatMapがある、と。それは確かにあなたが探しているものを行うための有効な方法です。

また、タグが^?と一致することを確認することもできます。

5

あなたは、タグ名を取り、その名前の終了タグのためのパーサを返す方法書くことができます:あなたはまたnodeが怠惰にする必要があり

object HTML extends RegexParsers {     
    lazy val node: Parser[Node] = text | element 
    val text: Parser[TextNode] = """[^<]+""".r ^^ TextNode 
    val label: Parser[String] = """(\w[:\w]*)""".r 
    val value : Parser[String] = """("[^"]*"|\w+)""".r 
    val attribute : Parser[(String, Option[String])] = label ~ (
     "=" ~> value ^^ Some[String] | "" ^^ { case _ => None } 
    ) ^^ { case (k ~ v) => k -> v } 

    val openTag: Parser[String ~ Seq[(String, Option[String])]] = 
    "<" ~> label ~ rep(whiteSpace ~> attribute) <~ ">" 

    def closeTag(name: String): Parser[String] = "</" ~> name <~ ">" 

    val element: Parser[Element] = openTag.flatMap { 
    case (tag ~ attrs) => 
     rep(node) <~ closeTag(tag) ^^ 
     (children => Element(tag, attrs.toMap, children)) 
    } 
} 

注意を。今、あなたは比類のないタグの素敵なきれいなエラーメッセージが出ます:

scala> HTML.parse(HTML.element, "<a></b>") 
res0: HTML.ParseResult[Element] = 
[1.6] failure: `a' expected but `b' found 

<a></b> 
    ^

を私は明瞭にするために、もう少し詳細な必要以上にしてきました。あなたはopenTagcloseTag方法をスキップして、例えば、このようなelementを書くことができます簡潔したい場合:

val element = "<" ~> label ~ rep(whiteSpace ~> attribute) <~ ">" >> { 
    case (tag ~ attrs) => 
    rep(node) <~ "</" ~> tag <~ ">" ^^ 
     (children => Element(tag, attrs.toMap, children)) 
} 

を私はより簡潔なバージョンが可能であると確信しているが、私の意見でも、これはunreadabilityに向かって縁取りされています。

関連する問題