2014-10-13 5 views
6

Instaparse (Clojure)에서 문맥 자유 문법을 사용하여 문자열을 파싱하기위한 프로젝트를 작성했습니다. 이제 파싱 결과에 대해 여러 입력 문자열을 테스트하고 싶습니다. 일부 입력 문자열은 문법에 맞지 않을 수 있습니다. 지금까지 나는 "기대에 부합하지 않는 파싱 된 문자열"만을 테스트했습니다. 하지만 (is (thrown? ...))을 사용하여 예외를 테스트하는 것이 더 정확할 것이라고 생각합니다. 예외가 발생 했습니까? 일부 출력 (포함 Parse error...)이 생성되지만 예외는 발생하지 않는 것으로 보입니다.Instaparse-grammar (Clojure)에 적합하지 않은 텍스트를 테스트하는 방법은 무엇입니까?

내 project.clj은 다음과 같습니다

(defproject com.stackoverflow.clojure/tests "0.1.0-SNAPSHOT" 
    :description "Tests of Clojure test-framework." 
    :url "http://example.com/FIXME" 
    :license {:name "Eclipse Public License" 
      :url "http://www.eclipse.org/legal/epl-v10.html"} 
    :dependencies [[org.clojure/clojure "1.6.0"] 
       [instaparse "1.3.4"]]) 

내 핵심 소스는 다음과 같습니다

(ns com.stackoverflow.clojure.testInstaparseWrongGrammar 
    (:require [instaparse.core :as insta])) 

(def parser (insta/parser " 
    <sentence> = words <DOT> 
    DOT  = '.' 
    <words> = word (<SPACE> word)* 
    SPACE  = ' ' 
    word  = #'(?U)\\w+' 
")) 

(defn formatter [expr] 
    (->> (parser expr) 
     (insta/transform {:word identity}) 
     (apply str))) 

내 테스트 소스는 다음과 같습니다

(ns com.stackoverflow.clojure.testInstaparseWrongGrammar-test 
    (:require [clojure.test :refer :all] 
      [com.stackoverflow.clojure.testInstaparseWrongGrammar :refer :all])) 

(deftest parser-tests 
    (is (= [[:word "Hello"] [:word "World"]] (parser "Hello World."))) 
    (is (not (= [[:word "Hello"] [:word "World"]] (parser "Hello World?")))) 
    ;(parser "Hello World?")  gives: 
    ; 
    ;Parse error at line 1, column 12: 
    ;Hello World? 
    ;   ^
    ;Expected one of: 
    ;"." (followed by end-of-string) 
    ;" " 
) 

(deftest formatter-tests 
    (is (= "HelloWorld" (formatter "Hello World."))) 
    (is (not (= "HelloWorld" (formatter "Hello World?")))) 
    ;(formatter "Hello World?")  gives: 
    ;"[:index 11][:reason [{:tag :string, :expecting \".\", :full true} {:tag :string, :expecting \" \"}]][:text \"Hello World?\"][:column 12][:line 1]" 
) 

; run the tests 
(run-tests) 

내가 여기 (오류를 테스트하는 방법 : 문장이 .으로 끝나지 않고 ! 인 경우)?

답변

6

Instaparse는 구문 분석 오류에서 예외를 throw하지 않습니다. 대신 "실패 개체"(ref : parse errors)를 반환합니다. (insta/failure? result)을 사용하여 장애 개체를 테스트 할 수 있습니다.

당신이 당신의 파서/포맷터가 예기치 않은 입력에 대한 예외를 throw 핵심에 그를 추가하려면 :

(ns com.stackoverflow.clojure.testInstaparseWrongGrammar 
    (:require [instaparse.core :as insta]) 
    (:require [instaparse.failure :as fail])) 

(def raw-parser (insta/parser " 
    <sentence> = words <DOT> 
    DOT  = '.' 
    <words> = word (<SPACE> word)* 
    SPACE  = ' ' 
    word  = #'(?U)\\w+' 
")) 

; pretty-print a failure as a string 
(defn- failure->string [result] 
    (with-out-str (fail/pprint-failure result))) 

; create an Exception with the pretty-printed failure message 
(defn- failure->exn [result] 
    (Exception. (failure->string result))) 

(defn parser [expr] 
    (let [result (raw-parser expr)] 
    (if (insta/failure? result) 
     (throw (failure->exn result)) 
     result))) 

(defn formatter [expr] 
    (->> (parser expr) 
     (insta/transform {:word identity}) 
     (apply str))) 

... 그리고 지금 당신이 시험에 (is (thrown? ...))를 사용할 수 있습니다

(deftest parser-tests 
    (is (= [[:word "Hello"] [:word "World"]] (parser "Hello World."))) 
    (is (thrown? Exception (= [[:word "Hello"] [:word "World"]] (parser "Hello World?")))) 

이 접근법은 instaparse를 사용하여 오류를 예쁘게 인쇄하고이를 예외로 줄 바꿈합니다. 또 다른 접근법은이 answer에 설명 된대로 ex-info을 사용하는 것입니다.

+0

오류 개체에서 정보를 얻으려면 어떻게해야합니까? 처음에는 두 가지 일을하고 싶습니다 (가능한 경우). ** 처음 ** : 라인 번호를 예외 메소드에 추가하십시오. ** 두 번째 ** : 멋지게 형식화 된 오류 메시지를 예외에 추가하십시오. ** 또한 **, 새로운 Exception 클래스를 생성하기 위해 Java에서 구현하는 것이 가장 쉬운 방법 인 것 같습니다 - 맞습니까? – Edward

+0

... '실패 개체'로 정확히 무엇을 의미합니까? 나는 Clojure에 Object (메소드와 변수 포함)가 없다고 생각했다. 그렇다면 어떻게 (일반적으로) 해당 객체의 메소드와 변수에 액세스 할 수 있습니까? – Edward

+0

@Edward 위의 코드는 예외의 구문 분석 오류 (줄, 열 등)에 대한 텍스트 설명을 포함합니다. "실패 객체"는 몇 가지 잘 알려진 키를 가진 맵 (기술적으로'defrecord'에 의해 생성 된'레코드')입니다; 예를 들어 행 번호는'(: line result)'로 접근 할 수 있습니다. – lnmx