Пользовательский генератор clojure.spec для объектов Java

Я только что видел одно из выступлений Рича о clojure.spec и очень хочу попробовать его в своем проекте. Я пишу серию инструментов для анализа кода C с использованием библиотеки eclipse CDT, и я хотел бы указать, что мои функции принимают и испускают объекты AST.

Я думаю, что очень простая спецификация может быть написана для функции, которая берет корень AST и испускает все листья дерева следующим образом:

(import '(org.eclipse.cdt.core.dom.ast IASTNode))
(require '[clojure.spec :as s])

(defn ast-node? [node] (instance? IASTNode node))
(s/def ::ast-node ast-node?)
(s/fdef leaves :args ::ast-node :ret (s/coll-of ::ast-node))

Однако, когда я пытаюсь выполнить код (s/exercise leaves) Я получаю ошибку:

Unable to construct gen at: [] for:  
xxx.x$leaves@xxx  
#:clojure.spec{:path [], :form #function[xxx.xxx/leaves], :failure :no-gen}

Как я могу написать собственный генератор для объектов Java, чтобы полностью специфицировать и использовать мой код?

2 ответа

Решение

Вы можете прикрепить собственный генератор к спецификации, используя s / with-gen. Вам нужно написать генератор, который будет производить все варианты узлов, которые вам нужны. Возможно, вам будет проще написать один генератор для каждого типа узла и затем объединить их, либо s/or или, возможно, используя что-то вроде s/multi-spec вместо этого (что сделало бы это открытым для расширения).

Пример написания генератора, который создает объект Java, будет выглядеть примерно так:

(s/def ::date 
  (s/with-gen #(instance? java.util.Date %)
    (fn [] (gen/fmap #(java.util.Date. %) (s/gen pos-int?)))))

fmap берет функцию и применяет ее к каждому результату от генератора, который вы ей даете. Если у вас есть Java-объект с конструктором, который принимает несколько значений, вы можете использовать генератор исходного кода, например (s/gen (s/tuple int? string? int?)),

Для полноты, вот мой код после применения ответа Алекса к спецификации узла AST "LiteralExpression":

(ns atom-finder.ast-spec
  (:import [org.eclipse.cdt.internal.core.dom.parser.cpp CPPASTLiteralExpression])
  (:require [clojure.spec :as s]
            [clojure.spec.gen :as gen]))

(def gen-literal-expression-args
 (gen/one-of
  [
   (gen/tuple (s/gen #{CPPASTLiteralExpression/lk_char_constant})
              (gen/char-ascii))
   (gen/tuple (s/gen #{CPPASTLiteralExpression/lk_float_constant})
              (gen/double))
   (gen/tuple (s/gen #{CPPASTLiteralExpression/lk_integer_constant})
              (s/gen (s/int-in -2147483648 2147483647)))
   (gen/tuple (s/gen #{CPPASTLiteralExpression/lk_string_literal})
              (gen/string))]))

(def gen-literal-expression
  (gen/fmap
   (fn [[type val]]
     (CPPASTLiteralExpression. type (.toCharArray (str val))))
   gen-literal-expression-args))

(s/def ::literal-expression
  (s/with-gen
    (partial instance? CPPASTLiteralExpression)
    (fn [] gen-literal-expression)))

(s/exercise :atom-finder.ast-spec/literal-expression 10
Другие вопросы по тегам