2009-08-28 4 views
5

간단한 Book DSL에 대한 스칼라 파서 결합 자 기능을 테스트하는 데 문제가 있습니다. - 일부가 보이지스칼라 파서 문제

object BookParser extends StandardTokenParsers { 
    lexical.reserved += ("book","has","isbn") 

    def bookSpec = "book" ~> stringLit ~> "has" ~> "isbn" ~> stringLit ^^ { 
      case "book" ~ name ~ "has" ~ "isbn" ~ isbn => new Book(name,isbn) } 

    def parse (s: String) = { 
    val tokens = new lexical.Scanner(s) 
    phrase(bookSpec)(tokens) 
    } 

    def test (exprString : String) = { 
    parse (exprString) match { 
     case Success(book) => println("Book"+book.getNiceName()) 
    } 
    } 

    def main (args: Array[String]) = { 
    test ("book ABC has isbn DEF") 
    } 
} 

나는 이것을 컴파일하려고 오류의 범위를 받고 있어요 :

case class Book (name:String,isbn:String) { 
def getNiceName():String = name+" : "+isbn 
} 

다음, 간단한 파서가 :

첫째 책 클래스가 인터넷에있는 다른 예제를 해체하려고 할 때 이상합니다. 예를 들어 bookSpec 함수는 다른 예제와 거의 동일하게 나타납니다.

이렇게하면 간단한 파서를 만드는 가장 좋은 방법일까요?

감사합니다.

답변

15

당신은 올바른 길을 가고 있습니다. 파서에 몇 가지 문제가 있습니다. 수정 된 코드를 게시 한 다음 변경 사항을 설명합니다. 파서에서 책을 반환하기 위해

import scala.util.parsing.combinator._ 
import scala.util.parsing.combinator.syntactical._ 

case class Book (name: String, isbn: String) { 
    def niceName = name + " : " + isbn 
} 


object BookParser extends StandardTokenParsers { 
    lexical.reserved += ("book","has","isbn") 

    def bookSpec: Parser[Book] = "book" ~ ident ~ "has" ~ "isbn" ~ ident ^^ { 
      case "book" ~ name ~ "has" ~ "isbn" ~ isbn => new Book(name, isbn) } 

    def parse (s: String) = { 
    val tokens = new lexical.Scanner(s) 
    phrase(bookSpec)(tokens) 
    } 

    def test (exprString : String) = { 
    parse (exprString) match { 
     case Success(book, _) => println("Book: " + book.niceName) 
     case Failure(msg, _) => println("Failure: " + msg) 
     case Error(msg, _) => println("Error: " + msg) 
    } 
    } 

    def main (args: Array[String]) = { 
    test ("book ABC has isbn DEF") 
    } 
} 

1. 파서 반환 값

, 당신은 도움이 inferencer 유형을 제공해야합니다. bookSpec 함수의 정의를 명시 적으로 변경했습니다. 파서 [Book]을 반환합니다. 즉, 책의 파서 인 객체를 반환합니다.

2 stringLit 당신이 사용하는 stringLit 기능은 StdTokenParsers의 특성에서 오는

. stringLit는 Parser [String]를 반환하는 함수이지만 일치하는 패턴에는 대부분의 언어에서 문자열 리터럴을 구분하는 데 사용하는 큰 따옴표가 포함됩니다. DSL에서 큰 따옴표로 묶는 단어에 만족하면 stringLit이 원하는 것입니다. 단순성을 위해 stringLit을 ident로 바꿨습니다. ident는 Java 언어 식별자를 찾습니다. 이것은 ISBN에 적합한 형식은 아니지만 테스트 케이스를 통과했습니다. :-)

ISBN을 정확하게 맞추려면 idents 대신 정규식을 사용해야한다고 생각합니다.

3. 귀하의 정규가 ~> 컴의 문자열을 사용

순서에서 왼쪽으로 무시. 이 함수는 두 개의 Parser [_] 객체를 사용하고 둘 다 차례로 인식 한 파서를 반환 한 다음 오른쪽 결과를 반환합니다. 전체 체인을 사용하여 최종 문자열로 이끄는 방식으로, 파서는 문장의 마지막 단어를 제외한 모든 것을 무시합니다. 즉, 책 이름도 버릴 수 있습니다.

또한 ~ ~ 또는 < ~을 사용하면 무시 된 토큰이 패턴 일치에 나타나서는 안됩니다.

간단히하기 위해이 모든 것을 간단한 시퀀스 함수로 변경하고 패턴 일치에 여분의 토큰을 남겼습니다.

매칭 4. 시험 방법은 파싱() 함수에서 모든 가능한 결과 일치해야

초래한다. 따라서 Failure() 및 Error() 사례를 추가했습니다.또한 성공한 경우에도 반환 값과 Reader 객체 모두이 포함됩니다. 우리는 독자를 신경 쓰지 않아 패턴 일치에서이를 무시하기 위해 "_"을 사용했습니다.

희망이 도움이됩니다.

+0

우수 대답은 당신을 감사합니다 - 현재 및 향후의 스칼라 모든 책을 통해 진행되고있다, 그리고이 두 가지보다 더 나은 답변입니다 :

"book" ~> stringLit // discards "book" "book" ~> stringLit ~> "has" // discards "book" and then stringLit "book" ~> stringLit ~> "has" ~> "isbn" // discards everything except "isbn" "book" ~> stringLit ~> "has" ~> "isbn" ~> stringLit // discards everything but the last stringLit 

는이처럼 쓸 수 나는 그것을 처리해야한다. (마틴 오더 스키 (Martin Odersky 's), Wampler & Payne (Wampler & Payne)) – ShaunL

6

~> 또는 <~을 사용하면 화살표가 나오는 요소가 삭제됩니다. 예를 들어 :

def bookSpec: Parser[Book] = ("book" ~> stringLit <~ "has" <~ "isbn") ~ stringLit ^^ { 
    case name ~ isbn => new Book(name,isbn) 
} 
+0

다니엘에게 대답 해 주셔서 감사합니다. mtnygard와 마찬가지로 대답은 매우 유용합니다. – ShaunL

+0

나는 이것이 잘못되었다고 생각합니다. 'stringLit <~ "has has"<~ "hasbn"~ stringLi' 이것은 "book"~ (stringLit <~ ("has < ~ ("isbn"~ stringLi)))'따라서 반환되지 않습니다. 스칼라 2.9.2로 테스트했습니다. –

+1

@ GuillaumeMassé 문제가 있습니다. 우선 순위는 잘못되었습니다. 그것은 단지'~'가'<~'보다 더 높은 우선 순위를 가지므로 라인의 끝은'''<~ ("isbn"~ stringLit)'이됩니다. 나는 그것을 피하기 위해 괄호 세트를 추가했습니다. 또한,이 모든 시간은 아마도 틀렸을 것입니다 - 언어에 우선 순위가 변경되지 않았습니다. –