2014-10-13 12 views
6

Ho scritto un progetto per l'analisi delle stringhe utilizzando la grammatica context-free in Instaparse (Clojure). Ora vorrei testare diverse stringhe di input per i loro risultati di analisi. Alcune stringhe di input potrebbero non rientrare nella grammatica. Finora ho provato solo per "stringhe analizzate che non rispondevano alle aspettative". Ma penso che sarebbe più accurato testare le eccezioni usando (is (thrown? ...)). Ci sono eccezioni lanciate? Mi sembra che venga generata qualche uscita (contenente Parse error...), ma non viene generata alcuna eccezione.Come testare i testi che non corrispondono ad una grammatica Instaparse (Clojure)?

mio 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"]]) 

La mia fonte principale è:

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

La mia fonte di prova è:

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

Come dovrei provare per gli errori (Qui : quando la frase non termina con un . ma con un !)?

risposta

6

Instaparse non genera un'eccezione per un errore di analisi; invece, restituisce un "oggetto fallimento" (rif: parse errors). È possibile verificare un oggetto guasto con (insta/failure? result).

Se si desidera che il parser/formattatore generare un'eccezione su ingresso inaspettato, aggiungere che al vostro core:

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

... e ora è possibile utilizzare (is (thrown? ...)) nel test:

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

Questo approccio utilizza instaparse per stampare correttamente l'errore e lo racchiude in un'eccezione. Un altro approccio è utilizzare ex-info come descritto in questo answer.

+0

Come ottengo le informazioni dall'oggetto failure? Per l'inizio mi piacerebbe fare due cose (se possibile). ** Primo **: aggiungi il numero di riga al mio metodo di eccezione. ** Secondo **: aggiungi il messaggio di errore ben formattato alla mia eccezione. ** Inoltre **, per creare una nuova classe Exception, sembra essere il modo più semplice per implementarlo in Java - è corretto? – Edward

+0

... e cosa intendi esattamente per 'oggetto fallimento'. Ho pensato che non ci fossero oggetti (con metodi e variabili) in Clojure. Quindi, come posso (in generale) accedere ai metodi e alle variabili di quegli oggetti? – Edward

+0

@Edward il codice sopra ora include una descrizione testuale dell'errore di analisi (riga, colonna, ecc.) Nell'eccezione. L '"oggetto fallimento" è una mappa (tecnicamente un "record" creato da "defrecord") che ha alcune chiavi ben note; per esempio, è possibile accedere al numero di riga con '(: line result)'. – lnmx

Problemi correlati