Как проверить тексты, не соответствующие грамматике Instaparse (Clojure)?
Я написал проект для разбора строк с использованием контекстно-свободной грамматики в Instaparse (Clojure). Теперь я хотел бы проверить несколько строк ввода для их результатов анализа. Некоторые входные строки могут не вписываться в грамматику. До сих пор я тестировал только для "проанализированных строк, не соответствующих ожиданиям". Но я думаю, что было бы точнее проверить исключения, используя (is (thrown? ...))
, Есть ли исключения? Мне кажется, что какой-то вывод (содержащий Parse error...
), но исключение не выдается.
Мой project.clj это:
(defproject com.stackru.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.stackru.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.stackru.clojure.testInstaparseWrongGrammar-test
(:require [clojure.test :refer :all]
[com.stackru.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)
Как я должен проверить на ошибки (Здесь: когда предложение не заканчивается .
но с !
)?
1 ответ
Instaparse не выдает исключение при ошибке разбора; вместо этого он возвращает "объект сбоя" (ref: parse errors). Вы можете проверить объект отказа с помощью (insta/failure? result)
,
Если вы хотите, чтобы ваш синтаксический анализатор / форматтер выдавал исключение при непредвиденном вводе, добавьте это в ваше ядро:
(ns com.stackru.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, чтобы красиво распечатать ошибку и обернуть ее в Исключение. Другой подход заключается в использовании ex-info
как указано в этом ответе.