2012-03-27 31 views
5

Tôi đang chơi đùa với một phân tích cú pháp toy HTML, để giúp bạn làm quen với bản thân mình combinators phân tích cú pháp thư viện của Scala:Scala: Phân tích phù hợp với thẻ

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) 
    }                       
}                        

Những gì tôi đang nhận ra tôi muốn là một số cách để đảm bảo tôi thẻ mở và đóng khớp với nhau.

Tôi nghĩ rằng, tôi cần một số loại tổ hợp flatMap combinator ~ Parser[A] => (A => Parser[B]) => Parser[B], để tôi có thể sử dụng thẻ mở để xây dựng trình phân tích cú pháp cho thẻ đóng. Nhưng tôi không thấy bất kỳ điều gì khớp với chữ ký đó in the library.

Cách thích hợp để thực hiện việc này là gì?

Trả lời

3

Bạn đang tìm kiếm địa điểm sai. Đó là một sai lầm bình thường, mặc dù. Bạn muốn có phương thức Parser[A] => (A => Parser[B]) => Parser[B], nhưng bạn đã xem tài liệu của Parsers, không phải Parser.

Look here.

Có một số flatMap, còn được gọi là into hoặc >>.

4

Có một flatMap trên Parser, và cũng là một phương pháp tương đương tên into và một nhà điều hành >>, mà có thể là bí danh thuận tiện hơn (flatMap vẫn cần thiết khi sử dụng trong cho comprehensions). Nó thực sự là một cách hợp lệ để làm những gì bạn đang tìm kiếm. Bạn cũng có thể kiểm tra xem các thẻ có khớp với ^? hay không.

5

Bạn có thể viết một phương pháp mà phải mất một tên thẻ và trả về một phân tích cú pháp cho một thẻ đóng với tên đó:

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)) 
    } 
} 

Lưu ý rằng bạn cũng cần phải thực hiện node lười biếng. Giờ đây, bạn nhận được thông báo lỗi sạch đẹp cho các thẻ chưa khớp:

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

<a></b> 
    ^

Tôi đã hiểu rõ hơn một chút so với mức cần thiết để rõ ràng. Nếu bạn muốn tính súc tích, bạn có thể bỏ qua phương pháp openTagcloseTag và viết element như thế này, ví dụ:

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

tôi chắc chắn rằng phiên bản ngắn gọn hơn sẽ là có thể, nhưng theo ý kiến ​​của tôi ngay cả điều này được viền phía unreadability.

Các vấn đề liên quan